package appservice import ( "encoding/json" "fmt" "strings" "net/http" "github.com/gorilla/mux" log "github.com/sirupsen/logrus" "git.deuxfleurs.fr/Deuxfleurs/easybridge/mxlib" "git.deuxfleurs.fr/Deuxfleurs/easybridge/connector" ) type Config struct { HttpBindAddr string Server string DbType string DbPath string MatrixDomain string } var registration *mxlib.Registration var config *Config func Start(r *mxlib.Registration, c *Config) (chan error, error) { registration = r config = c err := InitDb() if err != nil { return nil, err } err = mxRegisterUser(registration.SenderLocalpart) if mxe, ok := err.(*mxlib.MxError); !ok || mxe.ErrCode != "M_USER_IN_USE" { return nil, err } err = mxProfileDisplayname(ezbrMxId(), "Easybridge") if err != nil { return nil, err } router := mux.NewRouter() router.HandleFunc("/_matrix/app/v1/transactions/{txnId}", handleTxn) router.HandleFunc("/transactions/{txnId}", handleTxn) errch := make(chan error) go func() { log.Printf("Starting HTTP server on %s", config.HttpBindAddr) err := http.ListenAndServe(config.HttpBindAddr, checkTokenAndLog(router)) if err != nil { errch <- err } }() return errch, nil } func checkTokenAndLog(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { r.ParseForm() if strings.Join(r.Form["access_token"], "") != registration.HsToken { http.Error(w, "Wrong or no token provided", http.StatusUnauthorized) return } log.Printf("%s %s %s\n", r.RemoteAddr, r.Method, r.URL) handler.ServeHTTP(w, r) }) } func handleTxn(w http.ResponseWriter, r *http.Request) { if r.Method == "PUT" { var txn mxlib.Transaction err := json.NewDecoder(r.Body).Decode(&txn) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) log.Printf("JSON decode error: %s\n", err) return } log.Debugf("Got transaction %#v\n", txn) for i := range txn.Events { handleTxnEvent(&txn.Events[i]) } fmt.Fprintf(w, "{}\n") } else { http.Error(w, "Expected PUT request", http.StatusBadRequest) } } func handleTxnEvent(e *mxlib.Event) { if e.Type == "m.room.message" { ev := &connector.Event{ Type: connector.EVENT_MESSAGE, Text: e.Content["body"].(string), } typ := e.Content["msgtype"].(string) if typ == "m.emote" { ev.Type = connector.EVENT_MESSAGE } // Look up if this is a private message room if pm_room := dbIsPmRoom(e.RoomId); pm_room != nil { acct := FindAccount(pm_room.MxUserID, pm_room.AccountName) if acct != nil && e.Sender == pm_room.MxUserID { ev.Author = acct.Conn.User() ev.Recipient = pm_room.UserID acct.Conn.Send(ev) } } // Look up if this is a regular room if room := dbIsPublicRoom(e.RoomId); room != nil { acct := FindJoinedAccount(e.Sender, room.Protocol, room.RoomID) if acct != nil { ev.Author = acct.Conn.User() ev.Room = room.RoomID acct.Conn.Send(ev) } else { log.Debugf("Could not find room account for %s %s %s", e.Sender, room.Protocol, room.RoomID) } } } else if e.Type == "m.room.member" { ms := e.Content["membership"].(string) if ms == "leave" { // If leaving a PM room, we must delete it if pm_room := dbIsPmRoom(e.RoomId); pm_room != nil { them_mx := userMxId(pm_room.Protocol, pm_room.UserID) mxRoomLeaveAs(e.RoomId, them_mx) db.Delete(pm_room) } // If leaving a public room, leave from server as well if room := dbIsPublicRoom(e.RoomId); room != nil { acct := FindJoinedAccount(e.Sender, room.Protocol, room.RoomID) if acct != nil { acct.Conn.Leave(room.RoomID) // TODO: manage autojoin list, remove this room } else { log.Debugf("Could not find room account for %s %s %s", e.Sender, room.Protocol, room.RoomID) } } } } else if e.Type == "m.room.topic" { if room := dbIsPublicRoom(e.RoomId); room != nil { acct := FindJoinedAccount(e.Sender, room.Protocol, room.RoomID) if acct != nil { acct.Conn.SetRoomInfo(room.RoomID, &connector.RoomInfo{ Topic: e.Content["topic"].(string), }) } } } }