diff --git a/component/threema/account.go b/component/threema/account.go index f5e4726..c7ee62f 100644 --- a/component/threema/account.go +++ b/component/threema/account.go @@ -1,18 +1,55 @@ package threema -import "errors" +import ( + "github.com/o3ma/o3" -type ThreemaAccount struct { - ID string + "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 string) *ThreemaAccount { - return &ThreemaAccount{} -} - -func (a *ThreemaAccount) Send(to string, msg string) error { - if a.ID == "" { - return errors.New("It was not possible to send, becaouse we have no account for you.\nPlease generate one, by sending `generate` to gateway") +func (t *Threema) getAccount(jid *models.JID) *Account { + if a, ok := t.accountJID[jid.String()]; ok { + return a } - return nil + 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.Session = o3.NewSessionContext(tid) + a.send, a.recieve, err = a.Session.Run() + + // TODO error handling + if err != nil { + return nil + } + + t.accountJID[jid.String()] = a + t.accountTID[string(a.TID)] = a + return a +} + +func (a *Account) Send(to string, msg string) error { + return a.Session.SendTextMessage(to, msg, a.send) } diff --git a/component/threema/bot.go b/component/threema/bot.go new file mode 100644 index 0000000..7edcc61 --- /dev/null +++ b/component/threema/bot.go @@ -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" +} diff --git a/component/threema/main.go b/component/threema/main.go index 545d3d4..7715a20 100644 --- a/component/threema/main.go +++ b/component/threema/main.go @@ -7,34 +7,58 @@ import ( "gosrc.io/xmpp" "dev.sum7.eu/genofire/thrempp/component" + "dev.sum7.eu/genofire/thrempp/models" ) type Threema struct { component.Component - out chan xmpp.Packet + out chan xmpp.Packet + accountJID map[string]*Account + accountTID map[string]*Account } func NewThreema(config map[string]interface{}) (component.Component, error) { - return &Threema{}, nil + t := &Threema{ + out: make(chan xmpp.Packet), + accountJID: make(map[string]*Account), + accountTID: make(map[string]*Account), + } + // TODO load accounts on startup + return t, nil } func (t *Threema) Connect() (chan xmpp.Packet, error) { - t.out = make(chan xmpp.Packet) return t.out, nil } func (t *Threema) Send(packet xmpp.Packet) { switch p := packet.(type) { case xmpp.Message: - attrs := p.PacketAttrs - account := t.getAccount(attrs.From) - log.WithFields(map[string]interface{}{ - "from": attrs.From, - "to": attrs.To, - }).Debug(p.Body) - threemaID := strings.ToUpper(strings.Split(attrs.To, "@")[0]) - err := account.Send(threemaID, p.Body) - if err != nil { - msg := xmpp.NewMessage("chat", "", attrs.From, "", "en") + 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 } diff --git a/models/jid.go b/models/jid.go index b42a2aa..7ce2304 100644 --- a/models/jid.go +++ b/models/jid.go @@ -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{}) }