1package chat
2
3import (
4	"context"
5	"encoding/json"
6
7	"github.com/keybase/client/go/chat/globals"
8	"github.com/keybase/client/go/chat/types"
9	"github.com/keybase/client/go/chat/utils"
10	"github.com/keybase/client/go/protocol/chat1"
11	"github.com/keybase/client/go/protocol/gregor1"
12	"github.com/keybase/client/go/protocol/keybase1"
13)
14
15type ConvDevConversationBackedStorage struct {
16	globals.Contextified
17	utils.DebugLabeler
18
19	adminOnly bool
20	topicType chat1.TopicType
21	ri        func() chat1.RemoteInterface
22}
23
24var _ types.ConvConversationBackedStorage = &ConvDevConversationBackedStorage{}
25
26func NewConvDevConversationBackedStorage(g *globals.Context, topicType chat1.TopicType, adminOnly bool,
27	ri func() chat1.RemoteInterface) *ConvDevConversationBackedStorage {
28	return &ConvDevConversationBackedStorage{
29		Contextified: globals.NewContextified(g),
30		DebugLabeler: utils.NewDebugLabeler(g.ExternalG(), "ConvDevConversationBackedStorage", false),
31		adminOnly:    adminOnly,
32		ri:           ri,
33		topicType:    topicType,
34	}
35}
36
37func (s *ConvDevConversationBackedStorage) getMembersType(conv chat1.ConversationLocal) chat1.ConversationMembersType {
38	return conv.GetMembersType()
39	/*
40		TODO: might need this, not sure
41		mt := conv.GetMembersType()
42		switch mt {
43		case chat1.ConversationMembersType_IMPTEAMUPGRADE:
44			return chat1.ConversationMembersType_IMPTEAMNATIVE
45		default:
46			return mt
47		}
48	*/
49}
50
51func (s *ConvDevConversationBackedStorage) PutToKnownConv(ctx context.Context, uid gregor1.UID,
52	conv chat1.ConversationLocal, src interface{}) (err error) {
53	if s.adminOnly && !conv.ReaderInfo.UntrustedTeamRole.IsAdminOrAbove() {
54		return NewDevStoragePermissionDeniedError(conv.ReaderInfo.UntrustedTeamRole)
55	}
56	dat, err := json.Marshal(src)
57	if err != nil {
58		return err
59	}
60	if _, _, err = NewBlockingSender(s.G(), NewBoxer(s.G()), s.ri).Send(ctx, conv.GetConvID(),
61		chat1.MessagePlaintext{
62			ClientHeader: chat1.MessageClientHeader{
63				Conv:        conv.Info.Triple,
64				TlfName:     conv.Info.TlfName,
65				MessageType: chat1.MessageType_TEXT,
66			},
67			MessageBody: chat1.NewMessageBodyWithText(chat1.MessageText{
68				Body: string(dat),
69			}),
70		}, 0, nil, nil, nil); err != nil {
71		return err
72	}
73	// only do min writer role stuff for team convs
74	if conv.GetMembersType() != chat1.ConversationMembersType_TEAM {
75		return nil
76	}
77	minWriterUnset := conv.ConvSettings == nil ||
78		conv.ConvSettings.MinWriterRoleInfo == nil ||
79		conv.ConvSettings.MinWriterRoleInfo.Role != keybase1.TeamRole_ADMIN
80	if s.adminOnly && minWriterUnset {
81		arg := chat1.SetConvMinWriterRoleArg{
82			ConvID: conv.Info.Id,
83			Role:   keybase1.TeamRole_ADMIN,
84		}
85		_, err := s.ri().SetConvMinWriterRole(ctx, arg)
86		if err != nil {
87			return err
88		}
89	}
90	return nil
91}
92
93func (s *ConvDevConversationBackedStorage) Put(ctx context.Context, uid gregor1.UID,
94	convID chat1.ConversationID, name string, src interface{}) (err error) {
95	defer s.Trace(ctx, &err, "Put(%s)", name)()
96
97	var conv chat1.ConversationLocal
98	baseConv, err := utils.GetVerifiedConv(ctx, s.G(), uid, convID, types.InboxSourceDataSourceAll)
99	if err != nil {
100		return err
101	}
102	tlfname := baseConv.Info.TlfName
103	conv, _, err = NewConversation(ctx, s.G(), uid, tlfname, &name, s.topicType,
104		s.getMembersType(baseConv), keybase1.TLFVisibility_PRIVATE, nil, s.ri, NewConvFindExistingNormal)
105	if err != nil {
106		return err
107	}
108	return s.PutToKnownConv(ctx, uid, conv, src)
109}
110
111func (s *ConvDevConversationBackedStorage) GetFromKnownConv(ctx context.Context, uid gregor1.UID,
112	conv chat1.ConversationLocal, dest interface{}) (found bool, err error) {
113	defer s.Trace(ctx, &err, "GetFromKnownConv(%s)", conv.GetConvID())()
114	tv, err := s.G().ConvSource.Pull(ctx, conv.GetConvID(), uid, chat1.GetThreadReason_GENERAL, nil,
115		&chat1.GetThreadQuery{
116			MessageTypes: []chat1.MessageType{chat1.MessageType_TEXT},
117		}, &chat1.Pagination{Num: 1})
118	if err != nil {
119		return false, err
120	}
121	if len(tv.Messages) == 0 {
122		return false, nil
123	}
124	msg := tv.Messages[0]
125	if !msg.IsValid() {
126		return false, nil
127	}
128	body := msg.Valid().MessageBody
129	if !body.IsType(chat1.MessageType_TEXT) {
130		return false, nil
131	}
132
133	if conv.GetMembersType() == chat1.ConversationMembersType_TEAM && s.adminOnly {
134		if conv.ConvSettings == nil || conv.ConvSettings.MinWriterRoleInfo == nil {
135			return false, NewDevStorageAdminOnlyError("no conversation settings")
136		}
137		if conv.ConvSettings.MinWriterRoleInfo.Role != keybase1.TeamRole_ADMIN {
138			return false, NewDevStorageAdminOnlyError("minWriterRole was not admin")
139		}
140	}
141	if err = json.Unmarshal([]byte(body.Text().Body), dest); err != nil {
142		return false, err
143	}
144	if err = JoinConversation(ctx, s.G(), s.DebugLabeler, s.ri, uid, conv.GetConvID()); err != nil {
145		return false, err
146	}
147	return true, nil
148}
149
150func (s *ConvDevConversationBackedStorage) Get(ctx context.Context, uid gregor1.UID,
151	convID chat1.ConversationID, name string, dest interface{}, createConvIfMissing bool) (found bool, conv *chat1.ConversationLocal, err error) {
152	defer s.Trace(ctx, &err, "Get(%s)", name)()
153
154	baseConv, err := utils.GetVerifiedConv(ctx, s.G(), uid, convID, types.InboxSourceDataSourceAll)
155	if err != nil {
156		return false, conv, err
157	}
158	if !createConvIfMissing {
159		convs, err := FindConversations(ctx, s.G(), s.DebugLabeler, types.InboxSourceDataSourceAll, s.ri, uid,
160			baseConv.Info.TlfName, s.topicType, s.getMembersType(baseConv), keybase1.TLFVisibility_PRIVATE, name,
161			nil)
162		if err != nil {
163			return false, conv, err
164		}
165		if len(convs) == 0 {
166			return false, conv, nil
167		}
168		conv = &convs[0]
169	} else {
170		newconv, _, err := NewConversation(ctx, s.G(), uid, baseConv.Info.TlfName, &name, s.topicType,
171			s.getMembersType(baseConv), keybase1.TLFVisibility_PRIVATE, nil, s.ri, NewConvFindExistingNormal)
172		if err != nil {
173			return false, conv, err
174		}
175		conv = &newconv
176	}
177	found, err = s.GetFromKnownConv(ctx, uid, *conv, dest)
178	return found, conv, err
179}
180