func run(stopSignal chan bool) { logging.Info("main", "Starting server") if config.Load(getConfigPath()) != nil { logging.Fatal("main", "Configuration error") } logging.Info("main", "Configuration read, now starting '", config.GetServerName(), "'") registry.Initialise() data.Initialise() messenger.Initialise() plugin.Initialise(true) //true = load and run plugins from database web.Initialise() go web.Run() for { select { case <-stopSignal: logging.Info("main", "Got stop signal, finalizing now") //signaller.Stop() return default: time.Sleep(time.Millisecond * 400) } } }
func Initialise() { logging.Info("data", "Initialise()") trackingSetup() dbConn, err := sql.Open("postgres", "postgres://"+config.All().Database.Username+ ":"+config.All().Database.Password+ "@"+config.All().Database.Address+ "/"+config.All().Database.Name+ "?sslmode=require") if err != nil { logging.Error("data", "Error opening DB connection") logging.Error("data", "Error: ", err) tracking_notifyFault(err) } DB, err = gorm.Open("postgres", dbConn) DB.LogMode(true) if err != nil { logging.Error("data", "Error launching DB engine") logging.Error("data", "Error: ", err) } checkStructures(DB) //make sure that objects in the config BaseObjects are //existing, creating them if nessesary. for _, usr := range config.All().BaseObjects.AdminUsers { tmp := user.User{} DB.Where(&user.User{Username: usr.Username}).First(&tmp) if tmp.Username != usr.Username { //if the user was not found logging.Info("data", "Creating admin user: "******"data", "Could not create admin. ", err) } DB.Create(&user.User{Username: usr.Username, Permissions: []user.Permission{user.Permission{Name: user.PERM_ADMIN}}, AuthMethods: []user.AuthenticationMethod{authMethod}, }) } } logging.Info("data", "Initialisation finished.") }
func SqlQueryServer(ws *websocket.Conn) { dbConn, err := sql.Open("postgres", "postgres://"+config.All().Database.Username+ ":"+config.All().Database.Password+ "@"+config.All().Database.Address+ "/"+config.All().Database.Name+ "?sslmode=require") if err != nil { logging.Error("data-websock", "DB error: ", err.Error()) return } defer dbConn.Close() for { var data struct { Type string Query string } err := websocket.JSON.Receive(ws, &data) if err != nil { logging.Warning("data-websock", "Recieve error: ", err.Error()) return } //process message switch data.Type { case "query": logging.Info("data-websock", "Got Query: ", data.Query) doQuery(data.Query, ws, dbConn) } } }
func (s *Server) handleEvent(e Event) { defer func(event Event) { err := recover() if err != nil { logging.Error("signaller", "Recovered from error when handling event: ", event, "Error: ", err) debug.PrintStack() } }(e) switch e.event { case connected: //Client connected e.client.reply(rplMOTD, s.motd) logging.Info("signaller", "[EVENT] Client connected: ", e) case disconnected: //Client disconnected logging.Info("signaller", "[EVENT] Client disconnected: ", e) for channel := range e.client.channelMap { e.client.partChannel(channel, "Client disconnected") } delete(s.clientMap, e.client.key) //test this? case command: //Client send a command fields := strings.Fields(e.input) if len(fields) < 1 { return } if strings.HasPrefix(fields[0], ":") { fields = fields[1:] } command := strings.ToUpper(fields[0]) args := fields[1:] s.handleCommand(e.client, command, args) } }
func upgrade_v2(db gorm.DB) { // Remove IsExecutable and IsTemplate from Resource // Add Type column logging.Info("Migrating 1 => 2") db.Exec(`ALTER TABLE "resources" ADD COLUMN res_type char(3)`) // If something is executable, it stays that way. Otherwise it becomes a template db.Exec("UPDATE resources SET res_type = ? WHERE is_executable = ?", plugin.ResJavascriptCode, true) db.Exec("UPDATE resources SET res_type = ? WHERE is_executable = ?", plugin.ResTemplate, false) db.Exec(`ALTER TABLE "resources" ALTER COLUMN res_type SET NOT NULL`) db.Model(&plugin.Resource{}).DropColumn("is_executable") db.Model(&plugin.Resource{}).DropColumn("is_template") }
func (s *Server) Run() { for event := range s.eventChan { s.handleEvent(event) } //now stopping - eventChan closed logging.Info("signaller", "Killing client connections") for _, client := range s.clientMap { client.sendDisconnectedEvent = false client.disconnect() for _, chanObj := range client.channelMap { chanObj.clientMap = nil chanObj.modeMap = nil } } s.clientMap = nil s.operatorMap = nil s.channelMap = nil }
func upgrade_v3(db gorm.DB) { // Hash the passwords logging.Info("Migrating 2 => 3") var unhashed []user.AuthenticationMethod db.Where("method_type = ?", user.AUTH_PASSWD).Find(&unhashed) for i := 0; i < len(unhashed); i++ { hashed, err := user.HashedPasswordAuth(unhashed[i].Value) if err != nil { logging.Error("data", "Could not transform password. ID ", unhashed[i].ID) } else { hashed.UserID = unhashed[i].UserID db.Create(&hashed) db.Delete(&unhashed[i]) } } }
func main() { processSignal := make(chan os.Signal, 1) signal.Notify(processSignal, os.Interrupt, os.Kill, syscall.SIGHUP) chanStop := make(chan bool) shouldRun := true go func() { //goroutine to monitor OS signals for { s := <-processSignal //wait for signal from OS logging.Info("main", "Got OS signal: ", s) if s != syscall.SIGHUP { shouldRun = false } chanStop <- true } }() run(chanStop) //will run until signalled to stop from above goroutine time.Sleep(time.Millisecond * 100) }
//called during initialisation. Should make sure the schema is intact and up to date. func checkStructures(db gorm.DB) { version := getDbVersion(db) if version < currentDataVersion { logging.Info("data", fmt.Sprintf("Migrating from %v to %v", version, currentDataVersion)) logging.Info("data", "Auto migrating tables") autoMigrateTables() logging.Info("data", "Migrating DB") upgradeDb(db) logging.Info("data", "Setting Current Version") setDbVersion(currentDataVersion, db) logging.Info("data", "Migrations finished") } else { logging.Info("data", "DB up to date - no migration needed") } }
func (s *Server) handleCommand(client *Client, command string, args []string) { switch command { case "PING": client.reply(rplPong) case "INFO": client.reply(rplInfo, "CNC Rosella-IRCD Signaller component") case "VERSION": client.reply(rplVersion, VERSION) case "NICK": if len(args) < 1 { client.reply(errNoNick) return } newNick := args[0] //Check newNick is of valid formatting (regex) if nickRegexp.MatchString(newNick) == false { client.reply(errInvalidNick, newNick) return } if _, exists := s.clientMap[strings.ToLower(newNick)]; exists { client.reply(errNickInUse, newNick) return } //Protect the server name from being used if strings.ToLower(newNick) == strings.ToLower(s.name) { client.reply(errNickInUse, newNick) return } logging.Info("signaller", "Client connected set name: ", newNick) client.setNick(newNick) case "USER": if client.nick == "" { client.reply(rplKill, "Your nickname is already being used") client.disconnect() } else { client.reply(rplWelcome) client.registered = true } case "JOIN": if client.registered == false { client.reply(errNotReg) return } if len(args) < 1 { client.reply(errMoreArgs) return } if args[0] == "0" { //Quit all channels for channel := range client.channelMap { client.partChannel(channel, "Disconnecting") } return } channels := strings.Split(args[0], ",") for _, channel := range channels { //Join the channel if it's valid if channelRegexp.Match([]byte(channel)) { client.joinChannel(channel) } } case "PART": if client.registered == false { client.reply(errNotReg) return } if len(args) < 1 { client.reply(errMoreArgs) return } reason := strings.Join(args[1:], " ") channels := strings.Split(args[0], ",") for _, channel := range channels { //Part the channel if it's valid if channelRegexp.Match([]byte(channel)) { client.partChannel(channel, reason) } } case "PRIVMSG": if client.registered == false { client.reply(errNotReg) return } if len(args) < 2 { client.reply(errMoreArgs) return } message := strings.Join(args[1:], " ") channel, chanExists := s.channelMap[strings.ToLower(args[0])] client2, clientExists := s.clientMap[strings.ToLower(args[0])] if chanExists { if channel.mode.noExternal { if _, inChannel := channel.clientMap[strings.ToLower(client.nick)]; !inChannel { //Not in channel, not allowed to send client.reply(errCannotSend, args[0]) return } } if channel.mode.moderated { clientMode := channel.modeMap[strings.ToLower(client.nick)] if !clientMode.operator && !clientMode.voice { //It's moderated and we're not +v or +o, do nothing client.reply(errCannotSend, args[0]) return } } for _, c := range channel.clientMap { if c != client { c.reply(rplMsg, client.nick, args[0], message) } } } else if clientExists { client.reply(rplMsg, client.nick, client2.nick, message) } else { client.reply(errNoSuchNick, args[0]) } case "QUIT": if client.registered == false { client.reply(errNotReg) return } client.disconnect() case "TOPIC": if client.registered == false { client.reply(errNotReg) return } if len(args) < 1 { client.reply(errMoreArgs) return } channel, exists := s.channelMap[strings.ToLower(args[0])] if exists == false { client.reply(errNoSuchNick, args[0]) return } if len(args) == 1 { client.reply(rplTopic, channel.name, channel.topic) return } clientMode := channel.modeMap[strings.ToLower(client.nick)] if channel.mode.topicLocked && !clientMode.operator { client.reply(errNoPriv) return } if args[1] == ":" { channel.topic = "" for _, client := range channel.clientMap { client.reply(rplNoTopic, channel.name) } } else { topic := strings.Join(args[1:], " ") topic = strings.TrimPrefix(topic, ":") channel.topic = topic for _, client := range channel.clientMap { client.reply(rplTopic, channel.name, channel.topic) } } case "LIST": if client.registered == false { client.reply(errNotReg) return } if len(args) == 0 { chanList := make([]string, 0, len(s.channelMap)) for channelName, channel := range s.channelMap { if channel.mode.secret { if _, inChannel := channel.clientMap[strings.ToLower(client.nick)]; !inChannel { //Not in the channel, skip continue } } listItem := fmt.Sprintf("%s %d :%s", channelName, len(channel.clientMap), channel.topic) chanList = append(chanList, listItem) } client.reply(rplList, chanList...) } else { channels := strings.Split(args[0], ",") chanList := make([]string, 0, len(channels)) for _, channelName := range channels { if channel, exists := s.channelMap[strings.ToLower(channelName)]; exists { listItem := fmt.Sprintf("%s %d :%s", channelName, len(channel.clientMap), channel.topic) chanList = append(chanList, listItem) } } client.reply(rplList, chanList...) } case "OPER": if client.registered == false { client.reply(errNotReg) return } if len(args) < 2 { client.reply(errMoreArgs) return } username := args[0] password := args[1] if hashedPassword, exists := s.operatorMap[username]; exists { h := sha1.New() io.WriteString(h, password) pass := fmt.Sprintf("%x", h.Sum(nil)) if hashedPassword == pass { client.operator = true client.reply(rplOper) return } } client.reply(errPassword) case "KILL": if client.registered == false { client.reply(errNotReg) return } if client.operator == false { client.reply(errNoPriv) return } if len(args) < 1 { client.reply(errMoreArgs) return } nick := args[0] reason := strings.Join(args[1:], " ") client, exists := s.clientMap[strings.ToLower(nick)] if !exists { client.reply(errNoSuchNick, nick) return } client.reply(rplKill, client.nick, reason) client.disconnect() case "KICK": if client.registered == false { client.reply(errNotReg) return } if len(args) < 2 { client.reply(errMoreArgs) return } channelKey := strings.ToLower(args[0]) targetKey := strings.ToLower(args[1]) channel, channelExists := s.channelMap[channelKey] if !channelExists { client.reply(errNoSuchNick, args[0]) return } target, targetExists := channel.clientMap[targetKey] if !targetExists { client.reply(errNoSuchNick, args[1]) return } clientMode := channel.modeMap[client.key] if !clientMode.operator && !client.operator { client.reply(errNoPriv) return } reason := strings.Join(args[2:], " ") //It worked for _, client := range channel.clientMap { client.reply(rplKick, client.nick, channel.name, target.nick, reason) } delete(channel.clientMap, targetKey) delete(channel.modeMap, targetKey) delete(target.channelMap, channelKey) case "MODE": if client.registered == false { client.reply(errNotReg) return } if len(args) < 1 { client.reply(errMoreArgs) return } channelKey := strings.ToLower(args[0]) channel, channelExists := s.channelMap[channelKey] if !channelExists { client.reply(errNoSuchNick, args[0]) return } mode := channel.mode if len(args) == 1 { //No more args, they just want the mode client.reply(rplChannelModeIs, args[0], mode.String(), "") return } if cm, ok := channel.modeMap[strings.ToLower(client.nick)]; !ok || !cm.operator { //Not a channel operator. //If they're not an irc operator either, they'll fail if !client.operator { client.reply(errNoPriv) return } } hasClient := false var oldClientMode, newClientMode *ClientMode var targetClient *Client if len(args) >= 3 { clientKey := strings.ToLower(args[2]) oldClientMode, hasClient = channel.modeMap[clientKey] if hasClient { targetClient = channel.clientMap[clientKey] newClientMode = new(ClientMode) *newClientMode = *oldClientMode } } mod := strings.ToLower(args[1]) if strings.HasPrefix(mod, "+") { for _, char := range mod { switch char { case 's': mode.secret = true case 't': mode.topicLocked = true case 'm': mode.moderated = true case 'n': mode.noExternal = true case 'o': if hasClient { newClientMode.operator = true } case 'v': if hasClient { newClientMode.voice = true } } } } else if strings.HasPrefix(mod, "-") { for _, char := range mod { switch char { case 's': mode.secret = false case 't': mode.topicLocked = false case 'm': mode.moderated = false case 'n': mode.noExternal = false case 'o': if hasClient { newClientMode.operator = false } case 'v': if hasClient { newClientMode.voice = false } } } } if hasClient { *oldClientMode = *newClientMode } channel.mode = mode for _, client := range channel.clientMap { if hasClient { client.reply(rplChannelModeIs, channel.name, args[1], targetClient.nick) } else { client.reply(rplChannelModeIs, channel.name, args[1], "") } } default: client.reply(errUnknownCommand, command) } }
func autoMigrateTables() { logging.Info("data", "Checking structure: Users") DB.AutoMigrate(&user.User{}) logging.Info("data", "Checking structure: Permissions") DB.AutoMigrate(&user.Permission{}) user.Permission{}.Init(DB) logging.Info("data", "Checking structure: Emails") DB.AutoMigrate(&user.Email{}) logging.Info("data", "Checking structure: Addresses") DB.AutoMigrate(&user.Address{}) logging.Info("data", "Checking structure: AuthenticationMethods") DB.AutoMigrate(&user.AuthenticationMethod{}) logging.Info("data", "Checking structure: Sessions") DB.AutoMigrate(&session.Session{}) logging.Info("data", "Checking structure: Entity") DB.AutoMigrate(&entity.Entity{}) logging.Info("data", "Checking structure: EntityPivot") DB.AutoMigrate(&entity.EntityPivot{}) logging.Info("data", "Checking structure: EntityLocationRecord") DB.AutoMigrate(&entity.EntityLocationRecord{}) logging.Info("data", "Checking structure: EntityStatusRecord") DB.AutoMigrate(&entity.EntityStatusRecord{}) logging.Info("data", "Checking structure: EntityLogRecord") DB.AutoMigrate(&entity.EntityLogRecord{}) logging.Info("data", "Checking structure: EntityEvent") DB.AutoMigrate(&entity.EntityEvent{}) logging.Info("data", "Checking structure: Plugin") DB.AutoMigrate(&plugin.Plugin{}) logging.Info("data", "Checking structure: Resource") DB.AutoMigrate(&plugin.Resource{}) logging.Info("data", "Checking structure: Stmdata") DB.AutoMigrate(&stmdata.Stmdata{}) }