예제 #1
0
파일: main.go 프로젝트: twitchyliquid64/CNC
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)
		}
	}
}
예제 #2
0
파일: main.go 프로젝트: twitchyliquid64/CNC
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.")
}
예제 #3
0
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)
		}
	}
}
예제 #4
0
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)
	}
}
예제 #5
0
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")
}
예제 #6
0
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
}
예제 #7
0
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])
		}
	}
}
예제 #8
0
파일: main.go 프로젝트: twitchyliquid64/CNC
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)
}
예제 #9
0
//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")
	}
}
예제 #10
0
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)
	}
}
예제 #11
0
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{})
}