diff --git a/configuration/configuration.go b/configuration/configuration.go new file mode 100644 index 0000000..b297c54 --- /dev/null +++ b/configuration/configuration.go @@ -0,0 +1,98 @@ +package configuration + +import ( + "git.kingpenguin.tk/chteufleur/go-xmpp4steam.git/database" + "git.kingpenguin.tk/chteufleur/go-xmpp4steam.git/gateway" + "git.kingpenguin.tk/chteufleur/go-xmpp4steam.git/logger" + + "github.com/jimlawless/cfg" + + "os" + "strings" +) + +const ( + XdgDirectoryName = "xmpp4steam" + configurationFilePath = "xmpp4steam/xmpp4steam.conf" + + PathConfEnvVariable = "XDG_CONFIG_DIRS" + DefaultXdgConfigDirs = "/etc/xdg" + + PathDataEnvVariable = "XDG_DATA_DIRS" + DefaultXdgDataDirs = "/usr/local/share/:/usr/share/" + PreferedPathDataDir = "/usr/local/share" +) + +var ( + MapConfig = make(map[string]string) +) + +func Init() { + loadConfigFile() + + dataPathDir := locateDataDirPath() + database.DatabaseFile = dataPathDir + "/" + database.DatabaseFileName + database.Init() + + gateway.ServerAddrs = dataPathDir + "/" + gateway.ServerAddrs + gateway.SentryDirectory = dataPathDir + "/" + gateway.SentryDirectory + os.MkdirAll(gateway.SentryDirectory, 0700) +} + +func loadConfigFile() bool { + ret := false + envVariable := os.Getenv(PathConfEnvVariable) + if envVariable == "" { + envVariable = DefaultXdgConfigDirs + } + for _, path := range strings.Split(envVariable, ":") { + logger.Debug.Println("Try to find configuration file into " + path) + configFile := path + "/" + configurationFilePath + if _, err := os.Stat(configFile); err == nil { + // The config file exist + if cfg.Load(configFile, MapConfig) == nil { + // And has been loaded succesfully + logger.Info.Println("Find configuration file at " + configFile) + ret = true + break + } + } + } + return ret +} + +func locateDataDirPath() string { + ret := "" + isDirFound := false + envVariable := os.Getenv(PathDataEnvVariable) + if envVariable == "" { + envVariable = DefaultXdgDataDirs + } + for _, path := range strings.Split(envVariable, ":") { + logger.Debug.Printf("Try to find data base directory into " + path) + dbDir := path + "/" + XdgDirectoryName + if fi, err := os.Stat(dbDir); err == nil && fi.IsDir() { + // The database file exist + logger.Info.Printf("Find data base directory at " + dbDir) + isDirFound = true + ret = dbDir + break + } + } + + if !isDirFound { + if strings.Contains(envVariable, PreferedPathDataDir) { + ret = PreferedPathDataDir + "/" + XdgDirectoryName + } else { + ret = strings.Split(envVariable, ":")[0] + "/" + XdgDirectoryName + } + + if os.MkdirAll(ret, 0700) == nil { + logger.Info.Printf("Creating new data base directory at " + ret) + } else { + logger.Error.Printf("Fail to create data base directory at " + ret) + os.Exit(1) + } + } + return ret +} diff --git a/database/database.go b/database/database.go index 92fd1bf..0710272 100644 --- a/database/database.go +++ b/database/database.go @@ -2,22 +2,19 @@ package database import ( "database/sql" + "git.kingpenguin.tk/chteufleur/go-xmpp4steam.git/logger" _ "github.com/mattn/go-sqlite3" - "log" ) const ( - databaseFile = "go_xmpp4steam.db" + DatabaseFileName = "go_xmpp4steam.db" - createDatabaseStmt = "create table if not exists users (jid text not null primary key, steamLogin text, steamPwd text, debug int);" - insertDatabaseStmt = "insert into users (jid, steamLogin, steamPwd, debug) values(?, ?, ?, ?)" - deleteDatabaseStmt = "delete from users where jid=?" - selectDatabaseStmt = "select jid, steamLogin, steamPwd, debug from users where jid=?" - updateDatabaseStmt = "update users set steamLogin=?, steamPwd=?, debug=? where jid=?" - - LogInfo = "\t[SQLITE INFO]\t" - LogError = "\t[SQLITE ERROR]\t" - LogDebug = "\t[SQLITE DEBUG]\t" + createDatabaseStmt = "create table if not exists users (jid text not null primary key, steamLogin text, steamPwd text, debug int);" + insertDatabaseStmt = "insert into users (jid, steamLogin, steamPwd, debug) values(?, ?, ?, ?)" + deleteDatabaseStmt = "delete from users where jid=?" + selectDatabaseStmt = "select jid, steamLogin, steamPwd, debug from users where jid=?" + selectAllDatabaseStmt = "select jid, steamLogin, steamPwd, debug from users" + updateDatabaseStmt = "update users set steamLogin=?, steamPwd=?, debug=? where jid=?" ) type DatabaseLine struct { @@ -28,19 +25,24 @@ type DatabaseLine struct { } var ( - db = new(sql.DB) + db = new(sql.DB) + DatabaseFile = "" ) func init() { - d, err := sql.Open("sqlite3", databaseFile) +} + +func Init() { + logger.Info.Printf("Init database (file %s)", DatabaseFile) + d, err := sql.Open("sqlite3", DatabaseFile) if err != nil { - log.Printf("%sError on openning database", LogError, err) + logger.Error.Printf("Error on openning database", err) } db = d _, err = db.Exec(createDatabaseStmt) if err != nil { - log.Printf("%sFailed to create table", LogError, err) + logger.Error.Printf("Failed to create table", err) } } @@ -49,7 +51,7 @@ func Close() { } func (newLine *DatabaseLine) AddLine() bool { - log.Printf("%sAdd new line %v", LogInfo, newLine) + logger.Info.Printf("Add new line %v", newLine) isUserRegistred := getLine(newLine.Jid) != nil if isUserRegistred { @@ -58,7 +60,7 @@ func (newLine *DatabaseLine) AddLine() bool { stmt, err := db.Prepare(insertDatabaseStmt) if err != nil { - log.Printf("%sError on insert jid %s", LogError, newLine.Jid, err) + logger.Error.Printf("Error on insert jid %s", newLine.Jid, err) return false } defer stmt.Close() @@ -68,7 +70,7 @@ func (newLine *DatabaseLine) AddLine() bool { } _, err = stmt.Exec(newLine.Jid, newLine.SteamLogin, newLine.SteamPwd, debug) if err != nil { - log.Printf("%sError on creating SQL statement", LogError, err) + logger.Error.Printf("Error on creating SQL statement", err) return false } @@ -76,10 +78,10 @@ func (newLine *DatabaseLine) AddLine() bool { } func (newLine *DatabaseLine) UpdateLine() bool { - log.Printf("%sUpdate line %s", LogInfo, newLine.Jid) + logger.Info.Printf("Update line %s", newLine.Jid) stmt, err := db.Prepare(updateDatabaseStmt) if err != nil { - log.Printf("%sError on update ", LogError, err) + logger.Error.Printf("Error on update ", err) return false } defer stmt.Close() @@ -93,7 +95,7 @@ func (newLine *DatabaseLine) UpdateLine() bool { } _, err = stmt.Exec(newLine.SteamLogin, newLine.SteamPwd, debug, newLine.Jid) if err != nil { - log.Printf("%sError on updating SQL statement", LogError, err) + logger.Error.Printf("Error on updating SQL statement", err) return false } @@ -118,26 +120,26 @@ func RemoveLine(jid string) bool { line.Jid = jid line.UpdateLine() - log.Printf("%sRemove line %s", LogInfo, jid) + logger.Info.Printf("Remove line %s", jid) stmt, err := db.Prepare(deleteDatabaseStmt) if err != nil { - log.Printf("%sError on delete jid %s", LogError, jid, err) + logger.Error.Printf("Error on delete jid %s", jid, err) return false } defer stmt.Close() res, err := stmt.Exec(jid) if err != nil { - log.Printf("%sError on delete SQL statement", LogError, err) + logger.Error.Printf("Error on delete SQL statement", err) return false } affect, err := res.RowsAffected() if err != nil { - log.Printf("%sError on delete SQL statement", LogError, err) + logger.Error.Printf("Error on delete SQL statement", err) return false } if affect == 0 { - log.Printf("%sNo line affected", LogDebug) + logger.Debug.Printf("No line affected") return false } @@ -148,7 +150,7 @@ func GetLine(jid string) *DatabaseLine { ret := getLine(jid) if ret == nil || ret.SteamLogin == "" { - log.Printf("%sLine empty", LogDebug) + logger.Debug.Printf("Line empty") return nil } @@ -156,19 +158,19 @@ func GetLine(jid string) *DatabaseLine { } func getLine(jid string) *DatabaseLine { - log.Printf("%sGet line %s", LogInfo, jid) + logger.Info.Printf("Get line %s", jid) ret := new(DatabaseLine) stmt, err := db.Prepare(selectDatabaseStmt) if err != nil { - log.Printf("%sError on select line", LogError, err) + logger.Error.Printf("Error on select line", err) return nil } defer stmt.Close() debug := 0 err = stmt.QueryRow(jid).Scan(&ret.Jid, &ret.SteamLogin, &ret.SteamPwd, &debug) if err != nil { - log.Printf("%sError on select scan", LogError, err) + logger.Error.Printf("Error on select scan", err) return nil } if debug == 1 { @@ -181,18 +183,24 @@ func getLine(jid string) *DatabaseLine { } func GetAllLines() []DatabaseLine { - log.Printf("%sGet all lines", LogInfo) + logger.Info.Printf("Get all lines") var ret []DatabaseLine - rows, err := db.Query("select jid, steamLogin, steamPwd from users") + rows, err := db.Query(selectAllDatabaseStmt) if err != nil { - log.Printf("%sError on select query", LogError, err) + logger.Error.Printf("Error on select query", err) } defer rows.Close() for rows.Next() { user := new(DatabaseLine) - rows.Scan(&user.Jid, &user.SteamLogin, &user.SteamPwd) + debug := 0 + rows.Scan(&user.Jid, &user.SteamLogin, &user.SteamPwd, &debug) if user.SteamLogin != "" { + if debug == 1 { + user.Debug = true + } else { + user.Debug = false + } ret = append(ret, *user) } } diff --git a/gateway/gateway.go b/gateway/gateway.go index 3adbe48..e4a7d27 100644 --- a/gateway/gateway.go +++ b/gateway/gateway.go @@ -2,12 +2,20 @@ package gateway import ( "github.com/Philipp15b/go-steam" + "time" ) const ( + resource = "go-xmpp4steam" +) + +var ( SentryDirectory = "sentries/" - resource = "go-xmpp4steam" + XmppGroupUser = "Steam" + + RemoteRosterRequestPermission = "remote-roster-request-permission" + RemoteRosterRequestRoster = "remote-roster-request-roster" ) type GatewayInfo struct { @@ -22,11 +30,13 @@ type GatewayInfo struct { Deleting bool // XMPP - XMPP_JID_Client string - XMPP_Out chan interface{} - XMPP_Connected_Client map[string]bool - XMPP_Composing_Timers map[string]*time.Timer - DebugMessage bool + XMPP_JID_Client string + XMPP_Out chan interface{} + XMPP_Connected_Client map[string]bool + XMPP_Composing_Timers map[string]*time.Timer + DebugMessage bool + XMPP_IQ_RemoteRoster_Request map[string]string + AllowEditRoster bool } type StatusSteamFriend struct { @@ -45,11 +55,17 @@ func (g *GatewayInfo) SetSteamAuthCode(authCode string) { } func (g *GatewayInfo) Disconnect() { - g.XMPP_Disconnect() + go g.XMPP_Disconnect() go g.SteamDisconnect() + g.SteamConnecting = false } func (g *GatewayInfo) Delete() { g.Deleting = true + + if g.AllowEditRoster { + g.removeAllUserFromRoster() + } + g.Disconnect() } diff --git a/gateway/steam.go b/gateway/steam.go index 1dd6123..7abb57e 100644 --- a/gateway/steam.go +++ b/gateway/steam.go @@ -1,21 +1,18 @@ package gateway import ( + "git.kingpenguin.tk/chteufleur/go-xmpp4steam.git/logger" "github.com/Philipp15b/go-steam" "github.com/Philipp15b/go-steam/protocol/steamlang" "github.com/Philipp15b/go-steam/steamid" - "encoding/json" "fmt" "io/ioutil" - "log" "strconv" "time" ) const ( - serverAddrs = "servers.addr" - State_Offline = steamlang.EPersonaState_Offline State_Online = steamlang.EPersonaState_Online State_Busy = steamlang.EPersonaState_Busy @@ -24,28 +21,34 @@ const ( State_LookingToTrade = steamlang.EPersonaState_LookingToTrade State_LookingToPlay = steamlang.EPersonaState_LookingToPlay State_Max = steamlang.EPersonaState_Max +) - LogSteamInfo = "\t[STEAM INFO]\t" - LogSteamError = "\t[STEAM ERROR]\t" - LogSteamDebug = "\t[STEAM DEBUG]\t" +var ( + ServerAddrs = "servers.addr" ) func (g *GatewayInfo) SteamRun() { if g.Deleting { - log.Printf("%sDeleting gateway", LogSteamInfo) + logger.Info.Printf("[%s] Deleting gateway", g.XMPP_JID_Client) return } - log.Printf("%sRunning", LogSteamInfo) + logger.Info.Printf("[%s] Running", g.XMPP_JID_Client) + steam.InitializeSteamDirectory() g.setLoginInfos() - g.SteamClient = steam.NewClient() + if g.SteamClient == nil { + g.SteamClient = steam.NewClient() + } g.SteamConnecting = false g.SteamClient.ConnectionTimeout = 10 * time.Second g.mainSteam() - log.Printf("%sReach main method's end", LogSteamInfo) - go g.SteamRun() + logger.Info.Printf("[%s] Reach main method's end", g.XMPP_JID_Client) + return + g.Disconnect() + go g.XMPP_Disconnect() + g.Run() } func (g *GatewayInfo) mainSteam() { @@ -54,7 +57,7 @@ func (g *GatewayInfo) mainSteam() { case *steam.ConnectedEvent: // Connected on server g.SteamConnecting = false - log.Printf("%sConnected on Steam serveur", LogSteamDebug) + logger.Debug.Printf("[%s] Connected on Steam serveur", g.XMPP_JID_Client) g.SteamClient.Auth.LogOn(g.SteamLoginInfo) case *steam.MachineAuthUpdateEvent: @@ -66,26 +69,37 @@ func (g *GatewayInfo) mainSteam() { g.SendSteamPresence(steamlang.EPersonaState_Online) g.SendXmppMessage(XmppJidComponent, "", "Connected on Steam network") + case *steam.LoggedOffEvent: + logger.Error.Printf("[%s] LoggedOffEvent: ", g.XMPP_JID_Client, e) + g.SendXmppMessage(XmppJidComponent, "", fmt.Sprintf("Disconnected of Steam network (%v)", e)) + g.SteamConnecting = false + case steam.FatalErrorEvent: - log.Printf("%sFatalError: ", LogSteamError, e) - g.SendXmppMessage(XmppJidComponent, "", "Steam Fatal Error : "+e.Error()) + logger.Error.Printf("[%s] FatalError: ", g.XMPP_JID_Client, e) + g.SendXmppMessage(XmppJidComponent, "", fmt.Sprintf("Steam Fatal Error : %v", e)) g.DisconnectAllSteamFriend() - return + g.SteamConnecting = false + + case *steam.DisconnectedEvent: + logger.Info.Printf("[%s] Disconnected event", g.XMPP_JID_Client) + g.SendXmppMessage(XmppJidComponent, "", fmt.Sprintf("Steam Error : %v", e)) + g.DisconnectAllSteamFriend() + g.SteamConnecting = false case error: - log.Printf("%s", LogSteamError, e) + logger.Error.Printf("[%s] ", g.XMPP_JID_Client, e) g.SendXmppMessage(XmppJidComponent, "", "Steam Error : "+e.Error()) + case *steam.LogOnFailedEvent: + logger.Error.Printf("[%s] Login failed", g.XMPP_JID_Client, e) + g.SendXmppMessage(XmppJidComponent, "", fmt.Sprintf("Login failed : %v", e.Result)) + g.SteamConnecting = false + case *steam.ClientCMListEvent: - // Save servers addresses - b, err := json.Marshal(*e) - if err != nil { - log.Printf("%sFailed to json.Marshal() servers list", LogSteamError) - } else { - ioutil.WriteFile(serverAddrs, b, 0666) - } + // Doing nothing with server list case *steam.PersonaStateEvent: + logger.Debug.Printf("[%s] Received PersonaStateEvent: %v", g.XMPP_JID_Client, e) // Presenc received if _, ok := g.SteamClient.Social.Friends.GetCopy()[e.FriendId]; !ok { // Is not in friend list @@ -130,6 +144,7 @@ func (g *GatewayInfo) mainSteam() { g.SendXmppPresence(status, tpye, "", steamId+"@"+XmppJidComponent, gameName, name) case *steam.ChatMsgEvent: + logger.Debug.Printf("[%s] Received ChatMsgEvent: %v", g.XMPP_JID_Client, e) // Message received from := e.ChatterId.ToString() + "@" + XmppJidComponent if e.EntryType == steamlang.EChatEntryType_Typing { @@ -141,6 +156,7 @@ func (g *GatewayInfo) mainSteam() { } case *steam.ChatInviteEvent: + logger.Debug.Printf("[%s] Received ChatInviteEvent: %v", g.XMPP_JID_Client, e) // Invitation to play if fromFriend, ok := g.SteamClient.Social.Friends.GetCopy()[e.FriendChatId]; ok { messageToSend := fmt.Sprintf("Currently playing to « %s », would you like to join ?", fromFriend.GameName) @@ -148,7 +164,8 @@ func (g *GatewayInfo) mainSteam() { } default: - log.Printf("%s", LogSteamDebug, e) + logger.Debug.Printf("[%s] Steam unmatch event (Type: %T): %v", g.XMPP_JID_Client, e, e) + g.SendXmppMessage(XmppJidComponent, "", fmt.Sprintf("Steam unmatch event (Type: %T): %v", e, e)) } } } @@ -164,7 +181,7 @@ func (g *GatewayInfo) setLoginInfos() { if err == nil { g.SteamLoginInfo.SentryFileHash = sentryHash } - log.Printf("%sAuthentification of (%s, %s)", LogSteamDebug, g.XMPP_JID_Client, g.SteamLoginInfo.Username) + logger.Debug.Printf("Authentification of (%s, %s)", g.XMPP_JID_Client, g.SteamLoginInfo.Username) } func (g *GatewayInfo) IsSteamConnected() bool { @@ -175,43 +192,36 @@ func (g *GatewayInfo) IsSteamConnected() bool { ret = g.SteamClient.Connected() } } + logger.Debug.Printf("[%s] Is Steam connected (Connected: %v)", g.XMPP_JID_Client, ret) return ret } func (g *GatewayInfo) SteamConnect() { if g.IsSteamConnected() { - log.Printf("%sTry to connect, but already connected", LogSteamDebug) + logger.Debug.Printf("[%s] Try to connect, but already connected", g.XMPP_JID_Client) return } if g.SteamConnecting { - log.Printf("%sTry to connect, but currently connecting…", LogSteamDebug) + logger.Debug.Printf("[%s] Try to connect, but currently connecting…", g.XMPP_JID_Client) return } g.SteamConnecting = true - b, err := ioutil.ReadFile(serverAddrs) - if err == nil { - var toList steam.ClientCMListEvent - err := json.Unmarshal(b, &toList) - if err != nil { - log.Printf("%sFailed to json.Unmarshal() servers list", LogSteamError) - } else { - log.Printf("%sConnecting...", LogSteamInfo, toList.Addresses[0]) - g.SteamClient.ConnectTo(toList.Addresses[0]) - } - } else { - log.Printf("%sFailed to read servers list file", LogSteamError) - log.Printf("%sConnecting...", LogSteamInfo) - g.SteamClient.Connect() - } + go func() { + logger.Info.Printf("[%s] Connecting...", g.XMPP_JID_Client) + g.SendXmppMessage(XmppJidComponent, "", "Connecting...") + addr := g.SteamClient.Connect() + logger.Info.Printf("[%s] Connected on %v", g.XMPP_JID_Client, addr) + g.SendXmppMessage(XmppJidComponent, "", fmt.Sprintf("Connected on %v", addr)) + }() } func (g *GatewayInfo) SteamDisconnect() { if !g.IsSteamConnected() { - log.Printf("%sTry to disconnect, but already disconnected", LogSteamDebug) + logger.Debug.Printf("[%s] Try to disconnect, but already disconnected", g.XMPP_JID_Client) return } - log.Printf("%sSteam disconnect", LogSteamInfo) + logger.Info.Printf("[%s] Steam disconnect", g.XMPP_JID_Client) g.XMPP_Disconnect() g.DisconnectAllSteamFriend() @@ -219,6 +229,7 @@ func (g *GatewayInfo) SteamDisconnect() { } func (g *GatewayInfo) DisconnectAllSteamFriend() { + logger.Debug.Printf("[%s] Disconnect all Steam friend", g.XMPP_JID_Client) for sid, _ := range g.FriendSteamId { g.SendXmppPresence(Status_offline, Type_unavailable, "", sid+"@"+XmppJidComponent, "", "") delete(g.FriendSteamId, sid) @@ -239,22 +250,24 @@ func (g *GatewayInfo) SendSteamMessageLeaveConversation(steamId string) { func (g *GatewayInfo) sendSteamMessage(steamId, message string, chatEntryType steamlang.EChatEntryType) { if !g.IsSteamConnected() { - log.Printf("%sTry to send message, but disconnected", LogSteamDebug) + logger.Debug.Printf("[%s] Try to send message, but disconnected", g.XMPP_JID_Client) return } steamIdUint64, err := strconv.ParseUint(steamId, 10, 64) if err == nil { + logger.Debug.Printf("[%s] Send message to %v", g.XMPP_JID_Client, steamIdUint64) g.SteamClient.Social.SendMessage(steamid.SteamId(steamIdUint64), chatEntryType, message) } else { - log.Printf("%sFailed to get SteamId from %s", LogSteamError, steamId) + logger.Error.Printf("[%s] Failed to get SteamId from %s", g.XMPP_JID_Client, steamId) } } func (g *GatewayInfo) SendSteamPresence(status steamlang.EPersonaState) { if !g.IsSteamConnected() { - log.Printf("%sTry to send presence, but disconnected", LogSteamDebug) + logger.Debug.Printf("[%s] Try to send presence, but disconnected", g.XMPP_JID_Client) return } + logger.Debug.Printf("[%s] Send presence (Status: %v)", g.XMPP_JID_Client, status) g.SteamClient.Social.SetPersonaState(status) } diff --git a/gateway/xmpp.go b/gateway/xmpp.go index cb94376..e93d40d 100644 --- a/gateway/xmpp.go +++ b/gateway/xmpp.go @@ -2,9 +2,10 @@ package gateway import ( "git.kingpenguin.tk/chteufleur/go-xmpp.git/src/xmpp" + "git.kingpenguin.tk/chteufleur/go-xmpp4steam.git/logger" "github.com/Philipp15b/go-steam/protocol/steamlang" - "log" + "strconv" "strings" "time" ) @@ -29,16 +30,20 @@ const ( ActionConnexion = "action_xmpp_connexion" ActionDeconnexion = "action_xmpp_deconnexion" ActionMainMethodEnded = "action_xmpp_main_method_ended" - - LogXmppInfo = "\t[XMPP INFO]\t" - LogXmppError = "\t[XMPP ERROR]\t" - LogXmppDebug = "\t[XMPP DEBUG]\t" ) var ( XmppJidComponent = "" + + iqId = uint64(0) ) +func NextIqId() string { + iqId += 1 + ret := strconv.FormatUint(iqId, 10) + return ret +} + func (g *GatewayInfo) ReceivedXMPP_Presence(presence *xmpp.Presence) { if presence.Type == Type_error { return @@ -77,19 +82,7 @@ func (g *GatewayInfo) ReceivedXMPP_Presence(presence *xmpp.Presence) { if len(g.XMPP_Connected_Client) <= 0 { g.Disconnect() } - } else if presence.Type == Type_available { - go g.SteamConnect() - transfertPresence = true - } - - } else { - // Destination is Steam user - if presence.Type == Type_unavailable { - // Disconnect - if len(g.XMPP_Connected_Client) <= 0 { - g.Disconnect() - } - } else if presence.Type == Type_available { + } else { go g.SteamConnect() transfertPresence = true } @@ -118,7 +111,7 @@ func (g *GatewayInfo) ReceivedXMPP_Presence(presence *xmpp.Presence) { if g.IsSteamConnected() { g.SendSteamPresence(steamStatus) - g.SendXmppPresence(presence.Show, presence.Type, presence.From, "", presence.Status, "") + g.SendXmppPresence(presence.Show, presence.Type, "", "", presence.Status, "") } } } @@ -140,6 +133,52 @@ func (g *GatewayInfo) ReceivedXMPP_Message(message *xmpp.Message) { } } +func (g *GatewayInfo) ReceivedXMPP_IQ(iq *xmpp.Iq) bool { + ret := false + if g.XMPP_IQ_RemoteRoster_Request[iq.Id] == RemoteRosterRequestPermission { + delete(g.XMPP_IQ_RemoteRoster_Request, iq.Id) + + if iq.Type == xmpp.IQTypeError && iq.Error.Condition() == xmpp.ErrorForbidden { + g.AllowEditRoster = false + logger.Info.Printf("Set allow roster edition to %v", g.AllowEditRoster) + } else if iq.Type == xmpp.IQTypeSet && iq.PayloadName().Space == xmpp.NSRemoteRosterManager { + remoteRosterQuery := &xmpp.RemoteRosterManagerQuery{} + iq.PayloadDecode(remoteRosterQuery) + if remoteRosterQuery.Type == xmpp.RemoteRosterManagerTypeAllowed { + g.AllowEditRoster = true + } else if remoteRosterQuery.Type == xmpp.RemoteRosterManagerTypeRejected { + g.AllowEditRoster = false + } else { + g.AllowEditRoster = false + } + logger.Info.Printf("Set allow roster edition to %v", g.AllowEditRoster) + g.SendXmppMessage(XmppJidComponent, "", "Set allow roster edition to "+strconv.FormatBool(g.AllowEditRoster)) + } else { + logger.Info.Printf("Check roster edition authorisation by querying roster's user") + // Remote roster namespace may not be supported (like prosody), so we send a roster query + iqId := NextIqId() + g.XMPP_IQ_RemoteRoster_Request[iqId] = RemoteRosterRequestRoster + iqSend := &xmpp.Iq{Id: iqId, Type: xmpp.IQTypeGet, From: iq.To, To: iq.From} + iqSend.PayloadEncode(&xmpp.RosterQuery{}) + g.XMPP_Out <- iqSend + } + ret = true + + } else if g.XMPP_IQ_RemoteRoster_Request[iq.Id] == RemoteRosterRequestRoster { + delete(g.XMPP_IQ_RemoteRoster_Request, iq.Id) + if iq.Type == xmpp.IQTypeResult && iq.PayloadName().Space == xmpp.NSRoster { + g.AllowEditRoster = true + } else { + g.AllowEditRoster = false + } + logger.Info.Printf("Set allow roster edition to %v", g.AllowEditRoster) + g.SendXmppMessage(XmppJidComponent, "", "Set allow roster edition to "+strconv.FormatBool(g.AllowEditRoster)) + ret = true + } + + return ret +} + func (g *GatewayInfo) XMPP_Disconnect() { g.SendXmppPresence(Status_offline, Type_unavailable, "", "", "", "") } @@ -165,18 +204,53 @@ func (g *GatewayInfo) SendXmppPresence(status, tpye, to, from, message, nick str p.To = to } if from == "" { - // TODO add an option to allow message comming directly from the gateway p.From = XmppJidComponent + "/" + resource } else { p.From = from + "/" + resource } - log.Printf("%sSend presence %v", LogXmppInfo, p) - g.XMPP_Out <- p + if tpye == Type_subscribe && g.AllowEditRoster { + g.addUserIntoRoster(from, nick) + } else { + logger.Info.Printf("Send presence %v", p) + g.XMPP_Out <- p + } +} + +func (g *GatewayInfo) addUserIntoRoster(jid, nick string) { + iq := xmpp.Iq{To: g.XMPP_JID_Client, Type: xmpp.IQTypeSet, Id: NextIqId()} + query := &xmpp.RosterQuery{} + queryItem := &xmpp.RosterItem{JID: jid, Name: nick, Subscription: xmpp.RosterSubscriptionBoth} + queryItem.Groupes = append(queryItem.Groupes, XmppGroupUser) + query.Items = append(query.Items, *queryItem) + iq.PayloadEncode(query) + logger.Info.Printf("Add user into roster %v", iq) + g.XMPP_Out <- iq +} + +func (g *GatewayInfo) removeAllUserFromRoster() { + // Friends + for steamId, _ := range g.SteamClient.Social.Friends.GetCopy() { + iq := xmpp.Iq{To: g.XMPP_JID_Client, Type: xmpp.IQTypeSet, Id: NextIqId()} + query := &xmpp.RosterQuery{} + query.Items = append(query.Items, *&xmpp.RosterItem{JID: steamId.ToString() + "@" + XmppJidComponent, Subscription: xmpp.RosterSubscriptionRemove}) + iq.PayloadEncode(query) + logger.Info.Printf("Remove steam user roster") + g.XMPP_Out <- iq + } + // Myself + iq := xmpp.Iq{To: g.XMPP_JID_Client, Type: xmpp.IQTypeSet, Id: NextIqId()} + query := &xmpp.RosterQuery{} + query.Items = append(query.Items, *&xmpp.RosterItem{JID: g.SteamClient.SteamId().ToString() + "@" + XmppJidComponent, Subscription: xmpp.RosterSubscriptionRemove}) + iq.PayloadEncode(query) + logger.Info.Printf("Remove steam user roster") + g.XMPP_Out <- iq } func (g *GatewayInfo) SendXmppMessage(from, subject, message string) { g.sendXmppMessage(from, subject, message, &xmpp.Active{}) + return + g.stopComposingTimer(from) // Make inactive after 2 min if nothing happen @@ -188,11 +262,15 @@ func (g *GatewayInfo) SendXmppMessage(from, subject, message string) { func (g *GatewayInfo) SendXmppMessageLeaveConversation(from string) { g.sendXmppMessage(from, "", "", &xmpp.Gone{}) + return + g.stopComposingTimer(from) } func (g *GatewayInfo) SendXmppMessageComposing(from string) { g.sendXmppMessage(from, "", "", &xmpp.Composing{}) + return + g.stopComposingTimer(from) timer := time.AfterFunc(20*time.Second, func() { @@ -240,7 +318,7 @@ func (g *GatewayInfo) sendXmppMessage(from, subject, message string, chatState i m.Active = &xmpp.Active{} } - log.Printf("%sSend message %v", LogXmppInfo, m) + logger.Info.Printf("Send message %v", m) g.XMPP_Out <- m } } diff --git a/logger/logger.go b/logger/logger.go new file mode 100644 index 0000000..2cae8bf --- /dev/null +++ b/logger/logger.go @@ -0,0 +1,18 @@ +package logger + +import ( + "io" + "log" +) + +var ( + Info *log.Logger + Debug *log.Logger + Error *log.Logger +) + +func Init(infoHandle io.Writer, warningHandle io.Writer, errorHandle io.Writer) { + Info = log.New(infoHandle, "INFO : ", log.Ldate|log.Ltime|log.Lshortfile) + Debug = log.New(warningHandle, "DEBUG : ", log.Ldate|log.Ltime|log.Lshortfile) + Error = log.New(errorHandle, "ERROR : ", log.Ldate|log.Ltime|log.Lshortfile) +} diff --git a/main.go b/main.go index e2d4718..4e93544 100644 --- a/main.go +++ b/main.go @@ -1,51 +1,51 @@ package main import ( + "git.kingpenguin.tk/chteufleur/go-xmpp4steam.git/configuration" "git.kingpenguin.tk/chteufleur/go-xmpp4steam.git/database" "git.kingpenguin.tk/chteufleur/go-xmpp4steam.git/gateway" + "git.kingpenguin.tk/chteufleur/go-xmpp4steam.git/logger" "git.kingpenguin.tk/chteufleur/go-xmpp4steam.git/xmpp" - "github.com/jimlawless/cfg" - - "log" "os" "os/signal" + "strings" "syscall" "time" ) const ( - Version = "v1.1-dev" - configurationFilePath = "xmpp4steam.cfg" -) - -var ( - mapConfig = make(map[string]string) + Version = "v1.1-dev" ) func init() { + logger.Init(os.Stdout, os.Stdout, os.Stderr) + configuration.Init() + logger.Info.Println("Running go-xmpp4steam " + Version) xmpp.SoftVersion = Version - err := cfg.Load(configurationFilePath, mapConfig) - if err != nil { - log.Fatal("Failed to load configuration file.", err) - } // XMPP config - xmpp.Addr = mapConfig["xmpp_server_address"] + ":" + mapConfig["xmpp_server_port"] - xmpp.JidStr = mapConfig["xmpp_hostname"] - xmpp.Secret = mapConfig["xmpp_secret"] - xmpp.Debug = mapConfig["xmpp_debug"] == "true" + xmpp.Addr = configuration.MapConfig["xmpp_server_address"] + ":" + configuration.MapConfig["xmpp_server_port"] + xmpp.JidStr = configuration.MapConfig["xmpp_hostname"] + xmpp.Secret = configuration.MapConfig["xmpp_secret"] + xmpp.Debug = configuration.MapConfig["xmpp_debug"] == "true" + if configuration.MapConfig["xmpp_group"] != "" { + gateway.XmppGroupUser = configuration.MapConfig["xmpp_group"] + } gateway.XmppJidComponent = xmpp.JidStr - os.MkdirAll(gateway.SentryDirectory, 0700) + for _, admin := range strings.Split(configuration.MapConfig["xmpp_admins"], ";") { + xmpp.AdminUsers[admin] = true + } } func main() { + go xmpp.Run() + time.Sleep(1 * time.Second) allDbUsers := database.GetAllLines() for _, dbUser := range allDbUsers { xmpp.AddNewUser(dbUser.Jid, dbUser.SteamLogin, dbUser.SteamPwd, dbUser.Debug) } - go xmpp.Run() sigchan := make(chan os.Signal, 1) signal.Notify(sigchan, os.Interrupt) @@ -55,6 +55,6 @@ func main() { xmpp.Disconnect() - log.Println("Exit main()") + logger.Info.Println("Exit main()") time.Sleep(1 * time.Second) } diff --git a/xmpp/commands.go b/xmpp/commands.go index f4b56a8..dffad00 100644 --- a/xmpp/commands.go +++ b/xmpp/commands.go @@ -3,9 +3,11 @@ package xmpp import ( "git.kingpenguin.tk/chteufleur/go-xmpp.git/src/xmpp" "git.kingpenguin.tk/chteufleur/go-xmpp4steam.git/database" + "git.kingpenguin.tk/chteufleur/go-xmpp4steam.git/logger" - "log" + "fmt" "strings" + "time" ) const ( @@ -14,6 +16,8 @@ const ( CommandDisconnectSteam = "disconnectSteam" CommandRemoveRegistration = "removeRegistration" CommandToggleDebugMode = "toggleDebugMode" + CommandUptimeMode = "uptime" + CommandMessageBroadcast = "messageBroadcast" ) var ( @@ -23,7 +27,7 @@ var ( ) func execDiscoCommand(iq *xmpp.Iq) { - log.Printf("%sAd-Hoc Command", LogInfo) + logger.Info.Printf("Ad-Hoc Command") // Disco Ad-Hoc reply := iq.Response(xmpp.IQTypeResult) @@ -49,6 +53,13 @@ func execDiscoCommand(iq *xmpp.Iq) { discoItem.Item = append(discoItem.Item, *discoI) discoI = &xmpp.DiscoItem{JID: jid.Domain, Node: CommandToggleDebugMode, Name: "Toggle debug mode"} discoItem.Item = append(discoItem.Item, *discoI) + discoI = &xmpp.DiscoItem{JID: jid.Domain, Node: CommandUptimeMode, Name: "Uptime"} + discoItem.Item = append(discoItem.Item, *discoI) + } + + if AdminUsers[jidBareFrom] { + discoI := &xmpp.DiscoItem{JID: jid.Domain, Node: CommandMessageBroadcast, Name: "Broadcast a message"} + discoItem.Item = append(discoItem.Item, *discoI) } } @@ -57,7 +68,7 @@ func execDiscoCommand(iq *xmpp.Iq) { } func execDisco(iq *xmpp.Iq) { - log.Printf("%sDisco Feature", LogInfo) + logger.Info.Printf("Disco Feature") jidBareTo := strings.SplitN(iq.To, "/", 2)[0] discoInfoReceived := &xmpp.DiscoItems{} @@ -69,14 +80,18 @@ func execDisco(iq *xmpp.Iq) { discoInfo := &xmpp.DiscoInfo{} if jidBareTo == jid.Domain { + // Only gateway discoInfo.Identity = append(discoInfo.Identity, *identityGateway) discoInfo.Feature = append(discoInfo.Feature, xmpp.DiscoFeature{Var: xmpp.NodeAdHocCommand}) discoInfo.Feature = append(discoInfo.Feature, xmpp.DiscoFeature{Var: xmpp.NSJabberClient}) discoInfo.Feature = append(discoInfo.Feature, xmpp.DiscoFeature{Var: xmpp.NSRegister}) + discoInfo.Feature = append(discoInfo.Feature, xmpp.DiscoFeature{Var: xmpp.NSPing}) } else { + // Only steam users discoInfo.Identity = append(discoInfo.Identity, *identityClients) discoInfo.Feature = append(discoInfo.Feature, xmpp.DiscoFeature{Var: xmpp.NSChatStatesNotification}) } + // Both discoInfo.Feature = append(discoInfo.Feature, xmpp.DiscoFeature{Var: xmpp.NSDiscoInfo}) discoInfo.Feature = append(discoInfo.Feature, xmpp.DiscoFeature{Var: xmpp.NSDiscoItems}) @@ -99,10 +114,11 @@ func execDisco(iq *xmpp.Iq) { func execCommandAdHoc(iq *xmpp.Iq) { adHoc := &xmpp.AdHocCommand{} iq.PayloadDecode(adHoc) + jidBareFrom := strings.SplitN(iq.From, "/", 2)[0] if adHoc.SessionID == "" && adHoc.Action == xmpp.ActionAdHocExecute { // First step in the command - log.Printf("%sAd-Hoc command (Node : %s). First step.", LogInfo, adHoc.Node) + logger.Info.Printf("Ad-Hoc command (Node : %s). First step.", adHoc.Node) reply := iq.Response(xmpp.IQTypeResult) cmd := &xmpp.AdHocCommand{Node: adHoc.Node, Status: xmpp.StatusAdHocExecute, SessionID: xmpp.SessionID()} @@ -120,13 +136,13 @@ func execCommandAdHoc(iq *xmpp.Iq) { cmd.XForm = *cmdXForm } else if adHoc.Node == CommandDisconnectSteam { + // Command steam deconnection cmd.Status = xmpp.StatusAdHocCompleted cmdXForm := &xmpp.AdHocXForm{Type: xmpp.TypeAdHocResult, Title: "Force Steam deconnexion"} cmd.XForm = *cmdXForm note := &xmpp.AdHocNote{Type: xmpp.TypeAdHocNoteInfo} - jidBare := strings.SplitN(iq.From, "/", 2)[0] - g := MapGatewayInfo[jidBare] + g := MapGatewayInfo[jidBareFrom] if g != nil { g.Disconnect() note.Value = "Send deconnexion on Steam network" @@ -135,13 +151,13 @@ func execCommandAdHoc(iq *xmpp.Iq) { } cmd.Note = *note } else if adHoc.Node == CommandRemoveRegistration { + // Command remove registration cmd.Status = xmpp.StatusAdHocCompleted cmdXForm := &xmpp.AdHocXForm{Type: xmpp.TypeAdHocResult, Title: "Remove registration"} cmd.XForm = *cmdXForm note := &xmpp.AdHocNote{Type: xmpp.TypeAdHocNoteInfo} - jidBare := strings.SplitN(iq.From, "/", 2)[0] - if RemoveUser(jidBare) { + if RemoveUser(jidBareFrom) { note.Value = "Remove registration success." } else { note.Value = "Failed to remove your registration." @@ -149,16 +165,16 @@ func execCommandAdHoc(iq *xmpp.Iq) { cmd.Note = *note } else if adHoc.Node == CommandToggleDebugMode { + // Command toggle debug mode cmd.Status = xmpp.StatusAdHocCompleted cmdXForm := &xmpp.AdHocXForm{Type: xmpp.TypeAdHocResult, Title: "Toggle debug mode"} cmd.XForm = *cmdXForm note := &xmpp.AdHocNote{Type: xmpp.TypeAdHocNoteInfo} - jidBare := strings.SplitN(iq.From, "/", 2)[0] - dbUser := database.GetLine(jidBare) + dbUser := database.GetLine(jidBareFrom) if dbUser != nil { dbUser.Debug = !dbUser.Debug - g := MapGatewayInfo[jidBare] + g := MapGatewayInfo[jidBareFrom] ok := dbUser.UpdateLine() if ok && g != nil { g.DebugMessage = dbUser.Debug @@ -175,12 +191,29 @@ func execCommandAdHoc(iq *xmpp.Iq) { } cmd.Note = *note + } else if adHoc.Node == CommandUptimeMode { + // Command get uptime + cmd.Status = xmpp.StatusAdHocCompleted + cmdXForm := &xmpp.AdHocXForm{Type: xmpp.TypeAdHocResult, Title: "Uptime"} + cmd.XForm = *cmdXForm + deltaT := time.Since(startTime) + val := fmt.Sprintf("%dj %dh %dm %ds", int64(deltaT.Hours()/24), int64(deltaT.Hours())%24, int64(deltaT.Minutes())%60, int64(deltaT.Seconds())%60) + note := &xmpp.AdHocNote{Type: xmpp.TypeAdHocNoteInfo, Value: val} + + cmd.Note = *note + } else if adHoc.Node == CommandMessageBroadcast && AdminUsers[jidBareFrom] { + // Command send broadcast message + cmdXForm := &xmpp.AdHocXForm{Type: xmpp.TypeAdHocForm, Title: "Broadcast a message", Instructions: "Message to broadcast to all user."} + + field := &xmpp.AdHocField{Var: "message", Label: "Message", Type: xmpp.TypeAdHocFieldTextSingle} + cmdXForm.Fields = append(cmdXForm.Fields, *field) + cmd.XForm = *cmdXForm } reply.PayloadEncode(cmd) comp.Out <- reply } else if adHoc.Action == xmpp.ActionAdHocExecute || adHoc.Action == xmpp.ActionAdHocNext { // Last step in the command - log.Printf("%sAd-Hoc command (Node : %s). Last step.", LogInfo, adHoc.Node) + logger.Info.Printf("Ad-Hoc command (Node : %s). Last step.", adHoc.Node) reply := iq.Response(xmpp.IQTypeResult) cmd := &xmpp.AdHocCommand{Node: adHoc.Node, Status: xmpp.StatusAdHocCompleted, SessionID: adHoc.SessionID} @@ -200,8 +233,7 @@ func execCommandAdHoc(iq *xmpp.Iq) { } if authCode != "" { // Succeded - jidBare := strings.SplitN(iq.From, "/", 2)[0] - g := MapGatewayInfo[jidBare] + g := MapGatewayInfo[jidBareFrom] if g != nil { g.SetSteamAuthCode(authCode) note.Value = "Command succeded !" @@ -233,13 +265,39 @@ func execCommandAdHoc(iq *xmpp.Iq) { note.Value = "Failed because Steam login or Steam password is empty." } cmd.Note = *note + + } else if adHoc.Node == CommandMessageBroadcast && AdminUsers[jidBareFrom] { + cmdXForm := &xmpp.AdHocXForm{Type: xmpp.TypeAdHocResult, Title: "Broadcast a message"} + cmd.XForm = *cmdXForm + note := &xmpp.AdHocNote{Type: xmpp.TypeAdHocNoteInfo} + + // Command Auth Code + message := "" + fields := adHoc.XForm.Fields + for _, field := range fields { + if field.Var == "message" { + message = field.Value + break + } + } + if message != "" { + // Succeded + for userJID, _ := range MapGatewayInfo { + SendMessage(userJID, "", message) + } + note.Value = "Message sended to all registered users" + } else { + // Failed + note.Value = "There is no message to send" + } + cmd.Note = *note } reply.PayloadEncode(cmd) comp.Out <- reply } else if adHoc.Action == xmpp.ActionAdHocCancel { // command canceled - log.Printf("%sAd-Hoc command (Node : %s). Command canceled.", LogInfo, adHoc.Node) + logger.Info.Printf("Ad-Hoc command (Node : %s). Command canceled.", adHoc.Node) reply := iq.Response(xmpp.IQTypeResult) cmd := &xmpp.AdHocCommand{Node: adHoc.Node, Status: xmpp.StatusAdHocCanceled, SessionID: adHoc.SessionID} reply.PayloadEncode(cmd) @@ -272,9 +330,9 @@ func getUser(fields []xmpp.AdHocField, iq *xmpp.Iq) *database.DatabaseLine { } if steamLogin != "" { // Succeded - jidBare := strings.SplitN(iq.From, "/", 2)[0] + jidBareFrom := strings.SplitN(iq.From, "/", 2)[0] dbUser := new(database.DatabaseLine) - dbUser.Jid = jidBare + dbUser.Jid = jidBareFrom dbUser.SteamLogin = steamLogin dbUser.SteamPwd = steamPwd dbUser.Debug = false diff --git a/xmpp/xmpp.go b/xmpp/xmpp.go index 91a5ab0..6c3d064 100644 --- a/xmpp/xmpp.go +++ b/xmpp/xmpp.go @@ -4,8 +4,9 @@ import ( "git.kingpenguin.tk/chteufleur/go-xmpp.git/src/xmpp" "git.kingpenguin.tk/chteufleur/go-xmpp4steam.git/database" "git.kingpenguin.tk/chteufleur/go-xmpp4steam.git/gateway" + "git.kingpenguin.tk/chteufleur/go-xmpp4steam.git/logger" - "log" + "os" "strings" "time" ) @@ -14,10 +15,6 @@ const ( ActionConnexion = "action_xmpp_connexion" ActionDeconnexion = "action_xmpp_deconnexion" ActionMainMethodEnded = "action_xmpp_main_method_ended" - - LogInfo = "\t[XMPP COMPONENT INFO]\t" - LogError = "\t[XMPP COMPONENT ERROR]\t" - LogDebug = "\t[XMPP COMPONENT DEBUG]\t" ) var ( @@ -36,21 +33,25 @@ var ( Debug = true MapGatewayInfo = make(map[string]*gateway.GatewayInfo) + AdminUsers = make(map[string]bool) + startTime = time.Now() ) func Run() { - log.Printf("%sRunning", LogInfo) + logger.Info.Printf("Running") // Create stream and configure it as a component connection. jid = must(xmpp.ParseJID(JidStr)).(xmpp.JID) stream = must(xmpp.NewStream(Addr, &xmpp.StreamConfig{LogStanzas: Debug})).(*xmpp.Stream) comp = must(xmpp.NewComponentXMPP(stream, jid, Secret)).(*xmpp.XMPP) mainXMPP() - log.Printf("%sReach main method's end", LogInfo) + time.Sleep(1 * time.Second) + logger.Info.Printf("Reach XMPP Run method's end") go Run() } func mainXMPP() { + defer logger.Info.Printf("Reach main method's end") // Define xmpp out for all users for _, u := range MapGatewayInfo { u.XMPP_Out = comp.Out @@ -60,10 +61,14 @@ func mainXMPP() { switch v := x.(type) { case *xmpp.Presence: jidBareFrom := strings.SplitN(v.From, "/", 2)[0] + jidBareTo := strings.SplitN(v.To, "/", 2)[0] g := MapGatewayInfo[jidBareFrom] if g != nil { - log.Printf("%sPresence transfered to %s", LogDebug, jidBareFrom) - g.ReceivedXMPP_Presence(v) + if jidBareTo == jid.Domain { + // Forward only if presence is for componant, in order to to not spam set presence on Steam + logger.Debug.Printf("Presence transfered to %s", jidBareFrom) + go g.ReceivedXMPP_Presence(v) + } } else { if v.Type != gateway.Type_error && v.Type != gateway.Type_probe { SendPresence(gateway.Status_offline, gateway.Type_unavailable, jid.Domain, v.From, "Your are not registred", "") @@ -74,124 +79,154 @@ func mainXMPP() { jidBareFrom := strings.SplitN(v.From, "/", 2)[0] g := MapGatewayInfo[jidBareFrom] if g != nil { - log.Printf("%sMessage transfered to %s", LogDebug, jidBareFrom) - g.ReceivedXMPP_Message(v) + logger.Debug.Printf("Message transfered to %s", jidBareFrom) + go g.ReceivedXMPP_Message(v) } else { SendMessage(v.From, "", "Your are not registred. If you want to register, please, send an Ad-Hoc command.") } case *xmpp.Iq: + jidBareFrom := strings.SplitN(v.From, "/", 2)[0] jidBareTo := strings.SplitN(v.To, "/", 2)[0] - switch v.PayloadName().Space { - case xmpp.NSDiscoInfo: - execDisco(v) + g := MapGatewayInfo[jidBareFrom] + iqTreated := false + if g != nil { + logger.Debug.Printf("Iq transfered to %s", jidBareFrom) + iqTreated = g.ReceivedXMPP_IQ(v) + } - case xmpp.NSDiscoItems: - execDisco(v) + if !iqTreated { + switch v.PayloadName().Space { + case xmpp.NSDiscoInfo: + execDisco(v) - case xmpp.NodeAdHocCommand: - if jidBareTo == jid.Domain { - execCommandAdHoc(v) - } else { - sendNotSupportedFeature(v) - } + case xmpp.NSDiscoItems: + execDisco(v) - case xmpp.NSVCardTemp: - if jidBareTo == jid.Domain { - reply := v.Response(xmpp.IQTypeResult) - vcard := &xmpp.VCard{} - reply.PayloadEncode(vcard) - comp.Out <- reply - } else { - sendNotSupportedFeature(v) - } - - case xmpp.NSJabberClient: - if jidBareTo == jid.Domain { - reply := v.Response(xmpp.IQTypeResult) - reply.PayloadEncode(&xmpp.SoftwareVersion{Name: "go-xmpp4steam", Version: SoftVersion}) - comp.Out <- reply - } else { - sendNotSupportedFeature(v) - } - - case xmpp.NSRegister: - if jidBareTo == jid.Domain { - reply := v.Response(xmpp.IQTypeResult) - jidBareFrom := strings.SplitN(v.From, "/", 2)[0] - registerQuery := &xmpp.RegisterQuery{} - - if v.Type == xmpp.IQTypeGet { - registerQuery.Instructions = "Please provide your Steam login and password (Please, be aware that the given Steam account information will be saved into an un-encrypted SQLite database)." - - dbUser := database.GetLine(jidBareFrom) - if dbUser != nil { - // User already registered - registerQuery.Registered = &xmpp.RegisterRegistered{} - registerQuery.Username = dbUser.SteamLogin - registerQuery.XForm = *getXFormRegistration(dbUser.SteamLogin) - } else { - registerQuery.XForm = *getXFormRegistration("") - } - reply.PayloadEncode(registerQuery) - - } else if v.Type == xmpp.IQTypeSet { - v.PayloadDecode(registerQuery) - - if registerQuery.Remove != nil { - RemoveUser(jidBareFrom) - } else { - dbUser := getUser(registerQuery.XForm.Fields, v) - if dbUser != nil { - if dbUser.UpdateUser() { - AddNewUser(dbUser.Jid, dbUser.SteamLogin, dbUser.SteamPwd, dbUser.Debug) - } else { - reply.Type = xmpp.IQTypeError - reply.Error = xmpp.NewErrorWithCode("406", "modify", xmpp.ErrorNotAcceptable, "") - } - } else { - reply.Type = xmpp.IQTypeError - reply.Error = xmpp.NewErrorWithCode("409", "cancel", xmpp.ErrorConflict, "") - } - } + case xmpp.NodeAdHocCommand: + if jidBareTo == jid.Domain { + execCommandAdHoc(v) + } else { + sendNotSupportedFeature(v) } - comp.Out <- reply - } else { + + case xmpp.NSVCardTemp: + if jidBareTo == jid.Domain { + reply := v.Response(xmpp.IQTypeResult) + vcard := &xmpp.VCard{} + reply.PayloadEncode(vcard) + comp.Out <- reply + } else { + sendNotSupportedFeature(v) + } + + case xmpp.NSJabberClient: + if jidBareTo == jid.Domain { + reply := v.Response(xmpp.IQTypeResult) + reply.PayloadEncode(&xmpp.SoftwareVersion{Name: "go-xmpp4steam", Version: SoftVersion}) + comp.Out <- reply + } else { + sendNotSupportedFeature(v) + } + + case xmpp.NSRegister: + if jidBareTo == jid.Domain { + treatmentNSRegister(v) + } else { + sendNotSupportedFeature(v) + } + + case xmpp.NSRoster: + // Do nothing + + case xmpp.NSPing: + if jidBareTo == jid.Domain { + treatmentNSPing(v) + } else { + sendNotSupportedFeature(v) + } + + default: sendNotSupportedFeature(v) } - - default: - sendNotSupportedFeature(v) } default: - log.Printf("%srecv: %v", LogDebug, x) + logger.Debug.Printf("recv: %v", x) } } - - // Send deconnexion - SendPresence(gateway.Status_offline, gateway.Type_unavailable, "", "", "", "") } func must(v interface{}, err error) interface{} { if err != nil { - log.Fatal(LogError, err) + logger.Debug.Printf("%v", err) + os.Exit(1) } return v } -func sendNotSupportedFeature(iq *xmpp.Iq) { - reply := iq.Response(xmpp.IQTypeError) - reply.PayloadEncode(xmpp.NewError("cancel", xmpp.ErrorFeatureNotImplemented, "")) +func treatmentNSRegister(iq *xmpp.Iq) { + reply := iq.Response(xmpp.IQTypeResult) + jidBareFrom := strings.SplitN(iq.From, "/", 2)[0] + registerQuery := &xmpp.RegisterQuery{} + + if iq.Type == xmpp.IQTypeGet { + registerQuery.Instructions = "Please provide your Steam login and password (Please, be aware that the given Steam account information will be saved into an un-encrypted SQLite database)." + + dbUser := database.GetLine(jidBareFrom) + if dbUser != nil { + // User already registered + registerQuery.Registered = &xmpp.RegisterRegistered{} + registerQuery.Username = dbUser.SteamLogin + registerQuery.XForm = *getXFormRegistration(dbUser.SteamLogin) + } else { + registerQuery.XForm = *getXFormRegistration("") + } + reply.PayloadEncode(registerQuery) + + } else if iq.Type == xmpp.IQTypeSet { + iq.PayloadDecode(registerQuery) + + if registerQuery.Remove != nil { + RemoveUser(jidBareFrom) + } else { + dbUser := getUser(registerQuery.XForm.Fields, iq) + if dbUser != nil { + if dbUser.UpdateUser() { + AddNewUser(dbUser.Jid, dbUser.SteamLogin, dbUser.SteamPwd, dbUser.Debug) + } else { + reply.Type = xmpp.IQTypeError + reply.Error = xmpp.NewErrorWithCode("406", "modify", xmpp.ErrorNotAcceptable, "") + } + } else { + reply.Type = xmpp.IQTypeError + reply.Error = xmpp.NewErrorWithCode("409", "cancel", xmpp.ErrorConflict, "") + } + } + } comp.Out <- reply } +func treatmentNSPing(iq *xmpp.Iq) { + reply := iq.Response(xmpp.IQTypeResult) + comp.Out <- reply +} + +func sendNotSupportedFeature(iq *xmpp.Iq) { + if iq.Type != xmpp.IQTypeError && iq.Type != xmpp.IQTypeResult { + reply := iq.Response(xmpp.IQTypeError) + reply.PayloadEncode(xmpp.NewError("cancel", xmpp.ErrorFeatureNotImplemented, "")) + comp.Out <- reply + } +} + func Disconnect() { - log.Printf("%sXMPP disconnect", LogInfo) + logger.Info.Printf("XMPP disconnect") for _, u := range MapGatewayInfo { u.SteamDisconnect() } + comp.Close() } func SendPresence(status, tpye, from, to, message, nick string) { @@ -228,18 +263,18 @@ func SendMessage(to, subject, message string) { m.Subject = subject } - log.Printf("%sSenp message %v", LogInfo, m) + logger.Info.Printf("Senp message %v", m) comp.Out <- m } -func AddNewUser(jid, steamLogin, steamPwd string, debugMessage bool) { - log.Printf("%sAdd user %s to the map", LogInfo, jid) +func AddNewUser(jidUser, steamLogin, steamPwd string, debugMessage bool) { + logger.Info.Printf("Add user %s to the map (debug mode set to %v)", jidUser, debugMessage) g := new(gateway.GatewayInfo) g.SteamLogin = steamLogin g.SteamPassword = steamPwd - g.XMPP_JID_Client = jid - g.SentryFile = gateway.SentryDirectory + jid + g.XMPP_JID_Client = jidUser + g.SentryFile = gateway.SentryDirectory + jidUser g.FriendSteamId = make(map[string]*gateway.StatusSteamFriend) g.Deleting = false @@ -247,9 +282,20 @@ func AddNewUser(jid, steamLogin, steamPwd string, debugMessage bool) { g.XMPP_Connected_Client = make(map[string]bool) g.XMPP_Composing_Timers = make(map[string]*time.Timer) g.DebugMessage = debugMessage + g.XMPP_IQ_RemoteRoster_Request = make(map[string]string) + g.AllowEditRoster = false - MapGatewayInfo[jid] = g + MapGatewayInfo[jidUser] = g go g.Run() + + logger.Info.Printf("Check roster edition by asking with remote roster manager namespace") + // Ask if remote roster is allow + iqId := gateway.NextIqId() + g.XMPP_IQ_RemoteRoster_Request[iqId] = gateway.RemoteRosterRequestPermission + iq := xmpp.Iq{To: jidUser, From: jid.Domain, Type: xmpp.IQTypeGet, Id: iqId} + // iq.PayloadEncode(&xmpp.RosterQuery{}) + iq.PayloadEncode(&xmpp.RemoteRosterManagerQuery{Reason: "Manage contacts in the Steam contact list", Type: xmpp.RemoteRosterManagerTypeRequest}) + comp.Out <- iq } func RemoveUser(jidBare string) bool {