Merge branch 'wait-for-comp-fix' into 'master'

handle xmpp component

See merge request genofire/thrempp!1
This commit is contained in:
Martin/Geno 2019-05-31 19:52:18 +02:00
commit 20efd72da8
6 changed files with 360 additions and 3 deletions

View File

@ -31,6 +31,10 @@ func Load(configs []Config) {
log.WithField("type", config.Type).Panic(err)
}
config.comp = comp
err = config.Start()
if err != nil {
log.WithField("type", config.Type).Panic(err)
}
log.WithField("type", config.Type).Infof("component for %s started", config.Host)
}
}

View File

@ -0,0 +1,84 @@
package threema
import (
"strconv"
"github.com/bdlm/log"
"github.com/o3ma/o3"
"gosrc.io/xmpp"
"dev.sum7.eu/genofire/golang-lib/database"
"dev.sum7.eu/genofire/thrempp/models"
)
type Account struct {
models.AccountThreema
Session o3.SessionContext
send chan<- o3.Message
recieve <-chan o3.ReceivedMsg
}
func (t *Threema) getAccount(jid *models.JID) *Account {
if a, ok := t.accountJID[jid.String()]; ok {
return a
}
account := models.AccountThreema{}
database.Read.Where("xmpp_id = (?)",
database.Read.Table(jid.TableName()).Select("id").Where(map[string]interface{}{
"local": jid.Local,
"domain": jid.Domain,
}).QueryExpr()).First(&account)
var lsk [32]byte
copy(lsk[:], account.LSK[:])
tid, err := o3.NewThreemaID(string(account.TID), lsk, o3.AddressBook{})
// TODO error handling
if err != nil {
return nil
}
tid.Nick = o3.NewPubNick("xmpp:" + jid.String())
a := &Account{AccountThreema: account}
a.XMPP = *jid
a.Session = o3.NewSessionContext(tid)
a.send, a.recieve, err = a.Session.Run()
// TODO error handling
if err != nil {
return nil
}
go a.reciever(t.out)
t.accountJID[jid.String()] = a
t.accountTID[string(a.TID)] = a
return a
}
func (a *Account) reciever(out chan<- xmpp.Packet) {
for receivedMessage := range a.recieve {
if receivedMessage.Err != nil {
log.Warnf("Error Receiving Message: %s\n", receivedMessage.Err)
continue
}
switch msg := receivedMessage.Msg.(type) {
case o3.TextMessage:
sender := msg.Sender().String()
if string(a.TID) == sender {
continue
}
xMSG := xmpp.NewMessage("chat", sender, a.XMPP.String(), strconv.FormatUint(msg.ID(), 10), "en")
xMSG.Body = msg.Text()
out <- xMSG
case o3.DeliveryReceiptMessage:
// msg.MsgID()
}
}
}
func (a *Account) Send(to string, msg string) error {
return a.Session.SendTextMessage(to, msg, a.send)
}

61
component/threema/bot.go Normal file
View File

@ -0,0 +1,61 @@
package threema
import (
"fmt"
"github.com/bdlm/log"
"github.com/o3ma/o3"
"dev.sum7.eu/genofire/golang-lib/database"
"dev.sum7.eu/genofire/thrempp/models"
)
func (t *Threema) Bot(from *models.JID, request string) string {
server := o3.ThreemaRest{}
logger := log.WithFields(map[string]interface{}{
"type": "threema",
"jid": from.String(),
})
switch request {
case "generate":
// test if account already exists
account := t.getAccount(from)
if account != nil {
return fmt.Sprintf("you already has the threema account with id: %s", string(account.TID))
}
// create account
id, err := server.CreateIdentity()
if err != nil {
logger.Warnf("failed to generate: %s", err)
return fmt.Sprintf("failed to create a threema account: %s", err)
}
//TODO works it
if err := database.Read.Where(from).First(from); err != nil {
database.Write.Create(from)
}
// store account
a := models.AccountThreema{}
a.XMPPID = from.ID
a.TID = make([]byte, len(id.ID))
a.LSK = make([]byte, len(id.LSK))
copy(a.TID, id.ID[:])
copy(a.LSK, id.LSK[:])
database.Write.Create(&a)
// fetch account and connect
account = t.getAccount(from)
tid := string(account.TID)
if tid != "" {
logger.WithField("threema", tid).Info("generate")
return fmt.Sprintf("threema account with id: %s", tid)
}
logger.Warn("failed to generate")
return "failed to create a threema account"
}
return "command not supported"
}

View File

@ -1,13 +1,79 @@
package threema
import "dev.sum7.eu/genofire/thrempp/component"
import (
"strings"
"github.com/bdlm/log"
"gosrc.io/xmpp"
"dev.sum7.eu/genofire/golang-lib/database"
"dev.sum7.eu/genofire/thrempp/component"
"dev.sum7.eu/genofire/thrempp/models"
)
type Threema struct {
component.Component
out chan xmpp.Packet
accountJID map[string]*Account
accountTID map[string]*Account
}
func NewThreema(config map[string]interface{}) (component.Component, error) {
return &Threema{}, nil
return &Threema{
out: make(chan xmpp.Packet),
accountJID: make(map[string]*Account),
accountTID: make(map[string]*Account),
}, nil
}
func (t *Threema) Connect() (chan xmpp.Packet, error) {
var jids []*models.JID
database.Read.Find(&jids)
for _, jid := range jids {
a := t.getAccount(jid)
log.WithFields(map[string]interface{}{
"jid": jid.String(),
"threema": string(a.TID),
}).Debug("connected")
}
return t.out, nil
}
func (t *Threema) Send(packet xmpp.Packet) {
switch p := packet.(type) {
case xmpp.Message:
from := models.ParseJID(p.PacketAttrs.From)
to := models.ParseJID(p.PacketAttrs.To)
logger := log.WithFields(map[string]interface{}{
"from": from,
"to": to,
})
logger.Debug(p.Body)
if to.IsDomain() {
msg := xmpp.NewMessage("chat", "", from.String(), "", "en")
msg.Body = t.Bot(from, p.Body)
t.out <- msg
return
}
account := t.getAccount(from)
if account == nil {
msg := xmpp.NewMessage("chat", "", from.String(), "", "en")
msg.Body = "It was not possible to send, becouse we have no account for you.\nPlease generate one, by sending `generate` to this gateway"
t.out <- msg
return
}
threemaID := strings.ToUpper(to.Local)
if err := account.Send(threemaID, p.Body); err != nil {
msg := xmpp.NewMessage("chat", "", from.String(), "", "en")
msg.Body = err.Error()
t.out <- msg
}
default:
log.Warnf("unkown package%v", p)
}
}
func init() {

View File

@ -1,6 +1,7 @@
package component
import (
"github.com/bdlm/log"
"gosrc.io/xmpp"
)
@ -32,7 +33,104 @@ func (c *Config) Start() error {
return nil
}
func (c *Config) recieve(chan xmpp.Packet) {
func (c *Config) recieve(packets chan xmpp.Packet) {
logger := log.WithField("type", c.Type)
for packet := range packets {
switch p := packet.(type) {
case xmpp.Message:
if p.PacketAttrs.From == "" {
p.PacketAttrs.From = c.Host
} else {
p.PacketAttrs.From += "@" + c.Host
}
loggerMSG := logger.WithFields(map[string]interface{}{
"from": p.PacketAttrs.From,
"to": p.PacketAttrs.To,
})
loggerMSG.Debug(p.Body)
c.xmpp.Send(p)
default:
log.Warn("ignoring packet:", packet)
}
}
}
func (c *Config) sender() {
logger := log.WithField("type", c.Type)
for {
packet, err := c.xmpp.ReadPacket()
if err != nil {
logger.Panicf("connection closed%s", err)
return
}
switch p := packet.(type) {
case xmpp.IQ:
attrs := p.PacketAttrs
loggerIQ := logger.WithFields(map[string]interface{}{
"from": attrs.From,
"to": attrs.To,
})
switch inner := p.Payload[0].(type) {
case *xmpp.DiscoInfo:
loggerIQ.Debug("Disco Info")
if p.Type == "get" {
iq := xmpp.NewIQ("result", attrs.To, attrs.From, attrs.Id, "en")
var identity xmpp.Identity
if inner.Node == "" {
identity = xmpp.Identity{
Name: c.Type,
Category: "gateway",
Type: "service",
}
}
payload := xmpp.DiscoInfo{
Identity: identity,
Features: []xmpp.Feature{
{Var: "http://jabber.org/protocol/disco#info"},
{Var: "http://jabber.org/protocol/disco#item"},
},
}
iq.AddPayload(&payload)
_ = c.xmpp.Send(iq)
}
case *xmpp.DiscoItems:
loggerIQ.Debug("DiscoItems")
if p.Type == "get" {
iq := xmpp.NewIQ("result", attrs.To, attrs.From, attrs.Id, "en")
var payload xmpp.DiscoItems
if inner.Node == "" {
payload = xmpp.DiscoItems{
Items: []xmpp.DiscoItem{
{Name: c.Type, JID: c.Host, Node: "node1"},
},
}
}
iq.AddPayload(&payload)
_ = c.xmpp.Send(iq)
}
default:
logger.Debug("ignoring iq packet", inner)
xError := xmpp.Err{
Code: 501,
Reason: "feature-not-implemented",
Type: "cancel",
}
reply := p.MakeError(xError)
_ = c.xmpp.Send(&reply)
}
case xmpp.Message:
c.comp.Send(packet)
case xmpp.Presence:
logger.Debug("Received presence:", p.Type)
default:
logger.Debug("ignoring packet:", packet)
}
}
}

View File

@ -1,6 +1,8 @@
package models
import (
"regexp"
"github.com/jinzhu/gorm"
"dev.sum7.eu/genofire/golang-lib/database"
@ -16,6 +18,48 @@ func (j *JID) TableName() string {
return "jid"
}
func ParseJID(jidString string) (jid *JID) {
jidSplitTmp := jidRegex.FindAllStringSubmatch(jidString, -1)
if len(jidSplitTmp) != 1 {
return nil
}
jidSplit := jidSplitTmp[0]
return &JID{
Local: jidSplit[1],
Domain: jidSplit[2],
}
}
func (jid *JID) String() string {
if jid == nil {
return ""
}
str := jid.Domain
if jid.Local != "" {
str = jid.Local + "@" + str
}
return str
}
func (jid *JID) IsDomain() bool {
return jid != nil && jid.Local == "" && jid.Domain != ""
}
func GetJID(jidStr string) (jid *JID) {
jidS := ParseJID(jidStr)
err := database.Read.Where(jidS).First(jid).Error
if err != nil {
return nil
}
return
}
var jidRegex *regexp.Regexp
func init() {
jidRegex = regexp.MustCompile(`^(?:([^@/<>'\" ]+)@)?([^@/<>'\"]+)(?:/([^<>'\" ][^<>'\"]*))?$`)
database.AddModel(&JID{})
}