package localserver import ( "context" "encoding/json" "fmt" "os" "path/filepath" ) const ( LIST_ZONE_MEMBERS = "list_zone_members" LIST_ZONE_MEMBERS_RESPONSE = "list_zone_members_response" GET_USER = "get_user" MODIFY_USER_CHAT_RIGHTS = "modify_user_chat_rights" ADD_KNOWN_CHAT = "add_known_chat" REMOVE_KNOWN_CHAT = "remove_known_chat" ADD_KNOWN_AUDIO_CHANNEL = "add_known_audio_channel" REMOVE_KNOWN_AUDIO_CHANNEL = "remove_known_audio_channel" ADD_KNOWN_VIDEO_CHANNEL = "add_known_video_channel" REMOVE_KNOWN_VIDEO_CHANNEL = "remove_known_video_channel" ADD_USER = "add_user" REMOVE_USER = "remove_user" ) const ( NEW_ZONE_USER = "new_zone_user" REMOVED_ZONE_USER = "removed_zone_user" ) type ZoneUsersHandler struct { ZoneId string ZoneMembersId []string DataChannels map[string]*DataChannel Flag *uint32 Publishers []<-chan *ZoneRequest DB *ZoneUsersDBHandler reqChans []chan<- *ZoneRequest } type ZoneUserConfig struct { DefaultChatsRights string `json:"defaultChatsRights"` DefaultAudioChannelsRights string `json:"defaultAudioChannelsRights"` DefaultVideoChannelsRights string `json:"defaultVideoChannelsRights"` DefaultMediaRights string `json:"defaultMediaRights"` DefaultFileRights string `json:"defaultFileRights"` AdminRights string `json:"adminRights"` } func NewZoneUsersHandler(zoneId string, owner string, authorizedMembers []string, dataChannels map[string]*DataChannel, flag *uint32) (zoneUsersHandler *ZoneUsersHandler, err error) { _, err = os.ReadDir(filepath.Join("data", "zones", zoneId, "users")) var zoneUsersDBHandler *ZoneUsersDBHandler if err != nil { if os.IsNotExist(err) { logger.Printf("creating chat directory for zone %s...\n", zoneId) mkdirErr := os.MkdirAll(filepath.Join("data", "zones", zoneId, "users"), 0700) if mkdirErr != nil { return nil, mkdirErr } zoneUsersDBHandler, err = NewZoneUsersDBHandler(zoneId, owner, authorizedMembers, true) if err != nil { return } file, ferr := os.Create(filepath.Join("data", "zones", zoneId, "users", "usersConfig.json")) if ferr != nil { return nil, ferr } baseConfig := ZoneUserConfig{ DefaultChatsRights: "", DefaultAudioChannelsRights: "", DefaultVideoChannelsRights: "", DefaultMediaRights: "", DefaultFileRights: "", AdminRights: "", } bs, jsonErr := json.Marshal(baseConfig) if jsonErr != nil { return nil, jsonErr } if _, writeErr := file.WriteString(string(bs)); writeErr != nil { return nil, writeErr } _ = file.Close() if err != nil { return } } else { return } } else { zoneUsersDBHandler, err = NewZoneUsersDBHandler(zoneId, owner, authorizedMembers, false) } if err != nil { return } zoneUsersHandler = &ZoneUsersHandler{ ZoneId: zoneId, DataChannels: dataChannels, ZoneMembersId: authorizedMembers, Flag: flag, DB: zoneUsersDBHandler, } return } func (zuh *ZoneUsersHandler) Init(ctx context.Context, authorizedMembers []string) (err error) { users, err := zuh.DB.ListUsers(0, 1000) if err != nil { return } logger.Println(authorizedMembers) for _, user := range users { var contain bool for _, member := range authorizedMembers { if user.ID == member { contain = true break } } if !contain { logger.Println("userId", user.ID) if rerr := zuh.RemoveUser(user.ID); rerr != nil { logger.Println(rerr) } go func(userId string) { for _, rc := range zuh.reqChans { logger.Println("------------------------ sending to req chan ------------------------") rc <- &ZoneRequest{ ReqType: string(REMOVED_ZONE_AUTHORIZED_MEMBER), From: "node", Payload: map[string]interface{}{ "userId": userId, }, } } }(user.ID) } } return } func (zuh *ZoneUsersHandler) Subscribe(ctx context.Context, publisher <-chan *ZoneRequest) (reqChan chan *ZoneRequest, done chan struct{}, errCh chan error) { reqChan, done, errCh = make(chan *ZoneRequest), make(chan struct{}), make(chan error) zuh.reqChans = append(zuh.reqChans, reqChan) go func() { for { select { case <-ctx.Done(): done <- struct{}{} return case req := <-publisher: if err := zuh.handleZoneRequest(ctx, req); err != nil { errCh <- err } } } }() return } func (zuh *ZoneUsersHandler) AddUser(userId string) (err error) { newUser := &User{ ID: userId, Name: userId, ChatRights: "", AudioChannelRights: "", VideoChannelRights: "", MediaRights: "", FileRights: "", KnownChatsId: []string{}, KnownAudioChannelsId: []string{}, KnownVideoChannelsId: make([]string, 0), KnownMediaFolderId: make([]string, 0), KnownFileFolderId: make([]string, 0), } if err = zuh.DB.AddNewUser(newUser); err != nil { return } err = atomicallyExecute(zuh.Flag, func() (err error) { users, err := zuh.DB.ListUsers(0, 10000) if err != nil { return } bs, jsonErr := json.Marshal(&ZoneResponse{ Type: NEW_ZONE_USER, From: "node", To: "user", Payload: map[string]interface{}{ "user": newUser, }, }) if jsonErr != nil { return jsonErr } for _, user := range users { if _, ok := zuh.DataChannels[user.ID]; ok { _ = zuh.DataChannels[user.ID].DataChannel.SendText(string(bs)) } } return }) return } func (zuh *ZoneUsersHandler) RemoveUser(userId string) (err error) { if err = zuh.DB.DeleteUser(userId); err != nil { return } err = atomicallyExecute(zuh.Flag, func() (err error) { users, err := zuh.DB.ListUsers(0, 10000) if err != nil { return } bs, jsonErr := json.Marshal(&ZoneResponse{ Type: REMOVED_ZONE_USER, From: "node", To: "user", Payload: map[string]interface{}{ "userId": userId, }, }) if jsonErr != nil { return jsonErr } if _, ok := zuh.DataChannels[userId]; ok { _ = zuh.DataChannels[userId].DataChannel.SendText(string(bs)) } for _, user := range users { if _, ok := zuh.DataChannels[user.ID]; ok { _ = zuh.DataChannels[user.ID].DataChannel.SendText(string(bs)) } } return }) return } func (zuh *ZoneUsersHandler) AddKnownChat(chatId string, userId string) (err error) { user, err := zuh.DB.GetUser(userId) if err != nil { return } for _, id := range user.KnownChatsId { if id == chatId { err = fmt.Errorf("user already know channel %s", chatId) return } } user.KnownChatsId = append(user.KnownChatsId, chatId) if err = zuh.DB.ModifyUser(userId, user); err != nil { return } return } func (zuh *ZoneUsersHandler) RemoveKnownChat(chatId string, userId string) (err error) { user, err := zuh.DB.GetUser(userId) if err != nil { return } var index int var contain bool for i, id := range user.KnownChatsId { if id == chatId { index = i contain = true break } } if !contain { err = fmt.Errorf("does not know this chat") return } if len(user.KnownChatsId) < 2 { user.KnownChatsId = make([]string, 0) } else { user.KnownChatsId = append(user.KnownChatsId[:index], user.KnownChatsId[index+1:]...) } err = zuh.DB.ModifyUser(userId, user) return } func (zuh *ZoneUsersHandler) AddKnownAudioChannel(channelId string, userId string) (err error) { logger.Println("added new audio channel", channelId, userId) user, err := zuh.DB.GetUser(userId) if err != nil { return } for _, id := range user.KnownAudioChannelsId { if id == channelId { err = fmt.Errorf("user already know channel %s", channelId) return } } user.KnownAudioChannelsId = append(user.KnownAudioChannelsId, channelId) if err = zuh.DB.ModifyUser(userId, user); err != nil { return } return } func (zuh *ZoneUsersHandler) RemoveKnownAudioChannel(channelId string, userId string) (err error) { user, err := zuh.DB.GetUser(userId) if err != nil { return } var index int var contain bool for i, id := range user.KnownAudioChannelsId { if id == channelId { index = i contain = true break } } if !contain { err = fmt.Errorf("does not know this chat") return } if len(user.KnownAudioChannelsId) < 2 { user.KnownAudioChannelsId = make([]string, 0) } else { user.KnownAudioChannelsId = append(user.KnownAudioChannelsId[:index], user.KnownAudioChannelsId[index+1:]...) } err = zuh.DB.ModifyUser(userId, user) return } func (zuh *ZoneUsersHandler) AddKnownVideoChannel(channelId string, userId string) (err error) { logger.Println("added new audio channel", channelId, userId) user, err := zuh.DB.GetUser(userId) if err != nil { return } for _, id := range user.KnownVideoChannelsId { if id == channelId { err = fmt.Errorf("user already know channel %s", channelId) return } } user.KnownVideoChannelsId = append(user.KnownVideoChannelsId, channelId) if err = zuh.DB.ModifyUser(userId, user); err != nil { return } return } func (zuh *ZoneUsersHandler) RemoveKnownVideoChannel(channelId string, userId string) (err error) { user, err := zuh.DB.GetUser(userId) if err != nil { return } var index int var contain bool for i, id := range user.KnownVideoChannelsId { if id == channelId { index = i contain = true break } } if !contain { err = fmt.Errorf("does not know this chat") return } if len(user.KnownVideoChannelsId) < 2 { user.KnownVideoChannelsId = make([]string, 0) } else { user.KnownVideoChannelsId = append(user.KnownVideoChannelsId[:index], user.KnownVideoChannelsId[index+1:]...) } err = zuh.DB.ModifyUser(userId, user) return } func (zuh *ZoneUsersHandler) AddKnownFolder(folderId string, userId string) (err error) { logger.Println("added new known folder", folderId, userId) user, err := zuh.DB.GetUser(userId) if err != nil { return } for _, id := range user.KnownFileFolderId { if id == folderId { err = fmt.Errorf("user already know channel %s", folderId) return } } user.KnownVideoChannelsId = append(user.KnownVideoChannelsId, folderId) if err = zuh.DB.ModifyUser(userId, user); err != nil { return } return } func (zuh *ZoneUsersHandler) RemoveKnownFolder(folderId string, userId string) (err error) { user, err := zuh.DB.GetUser(userId) if err != nil { return } var index int var contain bool for i, id := range user.KnownFileFolderId { if id == folderId { index = i contain = true break } } if !contain { err = fmt.Errorf("does not know this chat") return } if len(user.KnownFileFolderId) < 2 { user.KnownFileFolderId = make([]string, 0) } else { user.KnownFileFolderId = append(user.KnownFileFolderId[:index], user.KnownFileFolderId[index+1:]...) } err = zuh.DB.ModifyUser(userId, user) return } func (zuh *ZoneUsersHandler) handleZoneRequest(ctx context.Context, req *ZoneRequest) (err error) { logger.Println("got request in zone users handler", req) switch req.ReqType { case REMOVE_USER: if err = verifyFieldsString(req.Payload, "userId"); err != nil { return } err = zuh.RemoveUser(req.Payload["userId"].(string)) case ADD_USER: if err = verifyFieldsString(req.Payload, "userId"); err != nil { return } err = zuh.AddUser(req.Payload["userId"].(string)) case REMOVE_KNOWN_CHAT: if err = verifyFieldsString(req.Payload, "chatId", "userId"); err != nil { return } err = zuh.RemoveKnownChat(req.Payload["chatId"].(string), req.Payload["userId"].(string)) case ADD_KNOWN_CHAT: if err = verifyFieldsString(req.Payload, "chatId", "userId"); err != nil { return } err = zuh.AddKnownChat(req.Payload["chatId"].(string), req.Payload["userId"].(string)) case REMOVE_KNOWN_AUDIO_CHANNEL: if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil { return } err = zuh.RemoveKnownAudioChannel(req.Payload["channelId"].(string), req.Payload["userId"].(string)) case ADD_KNOWN_AUDIO_CHANNEL: if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil { return } err = zuh.AddKnownAudioChannel(req.Payload["channelId"].(string), req.Payload["userId"].(string)) case REMOVE_KNOWN_VIDEO_CHANNEL: if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil { return } err = zuh.RemoveKnownVideoChannel(req.Payload["channelId"].(string), req.Payload["userId"].(string)) case ADD_KNOWN_VIDEO_CHANNEL: if err = verifyFieldsString(req.Payload, "channelId", "userId"); err != nil { return } err = zuh.AddKnownVideoChannel(req.Payload["channelId"].(string), req.Payload["userId"].(string)) case GET_USER: if err = verifyFieldsString(req.Payload, "userId"); err != nil { return } if _, ok := req.Payload["init"]; !ok { err = fmt.Errorf("no field init in req payload for get user") return } if _, ok := req.Payload["init"].(bool); !ok { err = fmt.Errorf("field init is of wrong type") return } user, err := zuh.DB.GetUser(req.Payload["userId"].(string)) if err != nil { return err } var answer *ZoneResponse if req.Payload["init"].(bool) { answer = &ZoneResponse{ Type: "get_current_user_response", To: "", From: "", Payload: map[string]interface{}{ "user": user, }, } } else { answer = &ZoneResponse{ Type: "get_user_response", To: "", From: "", Payload: map[string]interface{}{ "user": user, }, } } bs, err := json.Marshal(answer) if err != nil { return err } logger.Println(string(bs)) if _, ok := zuh.DataChannels[req.From]; ok { if err = zuh.DataChannels[req.From].DataChannel.SendText(string(bs)); err != nil { return err } } case LIST_ZONE_MEMBERS: users, err := zuh.DB.ListUsers(0, 0) if err != nil { return err } bs, err := json.Marshal(&ZoneResponse{ Type: LIST_ZONE_MEMBERS_RESPONSE, To: "", From: "", Payload: map[string]interface{}{ "users": users, }, }) if err != nil { return err } if _, ok := zuh.DataChannels[req.From]; ok { if err = zuh.DataChannels[req.From].DataChannel.SendText(string(bs)); err != nil { return err } } case MODIFY_USER_CHAT_RIGHTS: } return }