diff --git a/chatbot/chatbot.py b/chatbot/chatbot.py index 47f8f30b9..6b31978ab 100644 --- a/chatbot/chatbot.py +++ b/chatbot/chatbot.py @@ -6,6 +6,8 @@ import json from Queue import Queue import random +import signal +import sys import time import grpc @@ -13,8 +15,6 @@ import model_pb2 as pb import model_pb2_grpc as pbx -_ONE_DAY_IN_SECONDS = 60 * 60 * 24 - APP_NAME = "Tino-chatbot" VERSION = "0.13" @@ -60,6 +60,7 @@ def next_quote(): next_quote.idx = idx return quotes[idx] +# This is the class for the server-side gRPC endpoints class Plugin(pbx.PluginServicer): def Account(self, acc_event, context): action = None @@ -146,7 +147,8 @@ def client(addr, schema, secret, server, cookie_file_name): elif msg.HasField("pres"): # Wait for peers to appear online and subscribe to their topics if msg.pres.topic == 'me': - if msg.pres.what == pb.ServerPres.ON and subscriptions.get(msg.pres.src) == None: + if (msg.pres.what == pb.ServerPres.ON or msg.pres.what == pb.ServerPres.MSG) \ + and subscriptions.get(msg.pres.src) == None: client_post(subscribe(msg.pres.src)) elif msg.pres.what == pb.ServerPres.OFF and subscriptions.get(msg.pres.src) != None: client_post(leave(msg.pres.src)) @@ -163,7 +165,7 @@ def client(addr, schema, secret, server, cookie_file_name): def server(listen): - # Launch plugin: acception connection(s) from the Tinode server. + # Launch plugin server: acception connection(s) from the Tinode server. server = grpc.server(futures.ThreadPoolExecutor(max_workers=16)) pbx.add_PluginServicer_to_server(Plugin(), server) server.add_insecure_port(listen) @@ -207,6 +209,10 @@ def load_quotes(file_name): return len(quotes) +def exit_gracefully(signo, stack_frame): + print "Terminated with signal", signo + sys.exit(0) + if __name__ == '__main__': """Parse command-line arguments. Extract server host name, listen address, authentication scheme""" random.seed() @@ -243,6 +249,10 @@ def load_quotes(file_name): # Load random quotes from file print "Loaded {} quotes".format(load_quotes(args.quotes)) + # Add signal handlers + signal.signal(signal.SIGINT, exit_gracefully) + signal.signal(signal.SIGTERM, exit_gracefully) + # Start Plugin server server(args.listen) # Initialize and launch client diff --git a/pbx/model.pb.go b/pbx/model.pb.go index 047d9ce54..5654a44f3 100644 --- a/pbx/model.pb.go +++ b/pbx/model.pb.go @@ -8,6 +8,7 @@ It is generated from these files: model.proto It has these top-level messages: + Unused DefaultAcsMode AccessMode SetSub @@ -35,10 +36,13 @@ It has these top-level messages: ServerMeta ServerInfo ServerMsg + ServerResp Session - Topic - User ClientReq + TopicEvent + AccountEvent + SubscriptionEvent + MessageEvent */ package pbx @@ -86,6 +90,30 @@ func (x InfoNote) String() string { } func (InfoNote) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } +type Crud int32 + +const ( + Crud_CREATE Crud = 0 + Crud_UPDATE Crud = 1 + Crud_DELETE Crud = 2 +) + +var Crud_name = map[int32]string{ + 0: "CREATE", + 1: "UPDATE", + 2: "DELETE", +} +var Crud_value = map[string]int32{ + "CREATE": 0, + "UPDATE": 1, + "DELETE": 2, +} + +func (x Crud) String() string { + return proto.EnumName(Crud_name, int32(x)) +} +func (Crud) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + // What to delete, either "msg" to delete messages (default) or "topic" to delete the topic or "sub" // to delete a subscription to topic. type ClientDel_What int32 @@ -110,7 +138,7 @@ var ClientDel_What_value = map[string]int32{ func (x ClientDel_What) String() string { return proto.EnumName(ClientDel_What_name, int32(x)) } -func (ClientDel_What) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{16, 0} } +func (ClientDel_What) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{17, 0} } type ServerPres_What int32 @@ -158,7 +186,40 @@ var ServerPres_What_value = map[string]int32{ func (x ServerPres_What) String() string { return proto.EnumName(ServerPres_What_name, int32(x)) } -func (ServerPres_What) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{23, 0} } +func (ServerPres_What) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{24, 0} } + +// Plugin response codes +type ServerResp_RespCode int32 + +const ( + // Instruct Tinode server to continue with default processing of the client message. + ServerResp_CONTINUE ServerResp_RespCode = 0 + // Drop the message as if the client did not send it + ServerResp_DROP ServerResp_RespCode = 1 + // Respond to client with the provided srvmsg + ServerResp_RESPOND ServerResp_RespCode = 2 + // Replace client's original message with the provided clmsg then continue with + // processing. + ServerResp_REPLACE ServerResp_RespCode = 3 +) + +var ServerResp_RespCode_name = map[int32]string{ + 0: "CONTINUE", + 1: "DROP", + 2: "RESPOND", + 3: "REPLACE", +} +var ServerResp_RespCode_value = map[string]int32{ + "CONTINUE": 0, + "DROP": 1, + "RESPOND": 2, + "REPLACE": 3, +} + +func (x ServerResp_RespCode) String() string { + return proto.EnumName(ServerResp_RespCode_name, int32(x)) +} +func (ServerResp_RespCode) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{28, 0} } type Session_AuthLevel int32 @@ -185,7 +246,16 @@ var Session_AuthLevel_value = map[string]int32{ func (x Session_AuthLevel) String() string { return proto.EnumName(Session_AuthLevel_name, int32(x)) } -func (Session_AuthLevel) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{27, 0} } +func (Session_AuthLevel) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{29, 0} } + +// Dummy placeholder message +type Unused struct { +} + +func (m *Unused) Reset() { *m = Unused{} } +func (m *Unused) String() string { return proto.CompactTextString(m) } +func (*Unused) ProtoMessage() {} +func (*Unused) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } // Topic default access mode type DefaultAcsMode struct { @@ -196,7 +266,7 @@ type DefaultAcsMode struct { func (m *DefaultAcsMode) Reset() { *m = DefaultAcsMode{} } func (m *DefaultAcsMode) String() string { return proto.CompactTextString(m) } func (*DefaultAcsMode) ProtoMessage() {} -func (*DefaultAcsMode) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } +func (*DefaultAcsMode) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } func (m *DefaultAcsMode) GetAuth() string { if m != nil { @@ -223,7 +293,7 @@ type AccessMode struct { func (m *AccessMode) Reset() { *m = AccessMode{} } func (m *AccessMode) String() string { return proto.CompactTextString(m) } func (*AccessMode) ProtoMessage() {} -func (*AccessMode) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } +func (*AccessMode) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } func (m *AccessMode) GetWant() string { if m != nil { @@ -250,7 +320,7 @@ type SetSub struct { func (m *SetSub) Reset() { *m = SetSub{} } func (m *SetSub) String() string { return proto.CompactTextString(m) } func (*SetSub) ProtoMessage() {} -func (*SetSub) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } +func (*SetSub) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } func (m *SetSub) GetUserId() string { if m != nil { @@ -276,7 +346,7 @@ type SetDesc struct { func (m *SetDesc) Reset() { *m = SetDesc{} } func (m *SetDesc) String() string { return proto.CompactTextString(m) } func (*SetDesc) ProtoMessage() {} -func (*SetDesc) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } +func (*SetDesc) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } func (m *SetDesc) GetDefaultAcs() *DefaultAcsMode { if m != nil { @@ -307,7 +377,7 @@ type GetOpts struct { func (m *GetOpts) Reset() { *m = GetOpts{} } func (m *GetOpts) String() string { return proto.CompactTextString(m) } func (*GetOpts) ProtoMessage() {} -func (*GetOpts) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } +func (*GetOpts) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } func (m *GetOpts) GetIfModifiedSince() int64 { if m != nil { @@ -339,7 +409,7 @@ type BrowseOpts struct { func (m *BrowseOpts) Reset() { *m = BrowseOpts{} } func (m *BrowseOpts) String() string { return proto.CompactTextString(m) } func (*BrowseOpts) ProtoMessage() {} -func (*BrowseOpts) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } +func (*BrowseOpts) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } func (m *BrowseOpts) GetSinceId() int32 { if m != nil { @@ -389,7 +459,7 @@ type GetQuery struct { func (m *GetQuery) Reset() { *m = GetQuery{} } func (m *GetQuery) String() string { return proto.CompactTextString(m) } func (*GetQuery) ProtoMessage() {} -func (*GetQuery) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } +func (*GetQuery) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } func (m *GetQuery) GetWhat() string { if m != nil { @@ -429,7 +499,7 @@ type SetQuery struct { func (m *SetQuery) Reset() { *m = SetQuery{} } func (m *SetQuery) String() string { return proto.CompactTextString(m) } func (*SetQuery) ProtoMessage() {} -func (*SetQuery) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } +func (*SetQuery) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } func (m *SetQuery) GetDesc() *SetDesc { if m != nil { @@ -457,7 +527,7 @@ type ClientHi struct { func (m *ClientHi) Reset() { *m = ClientHi{} } func (m *ClientHi) String() string { return proto.CompactTextString(m) } func (*ClientHi) ProtoMessage() {} -func (*ClientHi) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } +func (*ClientHi) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } func (m *ClientHi) GetId() string { if m != nil { @@ -514,7 +584,7 @@ type ClientAcc struct { func (m *ClientAcc) Reset() { *m = ClientAcc{} } func (m *ClientAcc) String() string { return proto.CompactTextString(m) } func (*ClientAcc) ProtoMessage() {} -func (*ClientAcc) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } +func (*ClientAcc) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } func (m *ClientAcc) GetId() string { if m != nil { @@ -577,7 +647,7 @@ type ClientLogin struct { func (m *ClientLogin) Reset() { *m = ClientLogin{} } func (m *ClientLogin) String() string { return proto.CompactTextString(m) } func (*ClientLogin) ProtoMessage() {} -func (*ClientLogin) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } +func (*ClientLogin) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } func (m *ClientLogin) GetId() string { if m != nil { @@ -613,7 +683,7 @@ type ClientSub struct { func (m *ClientSub) Reset() { *m = ClientSub{} } func (m *ClientSub) String() string { return proto.CompactTextString(m) } func (*ClientSub) ProtoMessage() {} -func (*ClientSub) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } +func (*ClientSub) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } func (m *ClientSub) GetId() string { if m != nil { @@ -653,7 +723,7 @@ type ClientLeave struct { func (m *ClientLeave) Reset() { *m = ClientLeave{} } func (m *ClientLeave) String() string { return proto.CompactTextString(m) } func (*ClientLeave) ProtoMessage() {} -func (*ClientLeave) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } +func (*ClientLeave) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } func (m *ClientLeave) GetId() string { if m != nil { @@ -688,7 +758,7 @@ type ClientPub struct { func (m *ClientPub) Reset() { *m = ClientPub{} } func (m *ClientPub) String() string { return proto.CompactTextString(m) } func (*ClientPub) ProtoMessage() {} -func (*ClientPub) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } +func (*ClientPub) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } func (m *ClientPub) GetId() string { if m != nil { @@ -735,7 +805,7 @@ type ClientGet struct { func (m *ClientGet) Reset() { *m = ClientGet{} } func (m *ClientGet) String() string { return proto.CompactTextString(m) } func (*ClientGet) ProtoMessage() {} -func (*ClientGet) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } +func (*ClientGet) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} } func (m *ClientGet) GetId() string { if m != nil { @@ -768,7 +838,7 @@ type ClientSet struct { func (m *ClientSet) Reset() { *m = ClientSet{} } func (m *ClientSet) String() string { return proto.CompactTextString(m) } func (*ClientSet) ProtoMessage() {} -func (*ClientSet) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} } +func (*ClientSet) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} } func (m *ClientSet) GetId() string { if m != nil { @@ -809,7 +879,7 @@ type ClientDel struct { func (m *ClientDel) Reset() { *m = ClientDel{} } func (m *ClientDel) String() string { return proto.CompactTextString(m) } func (*ClientDel) ProtoMessage() {} -func (*ClientDel) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} } +func (*ClientDel) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} } func (m *ClientDel) GetId() string { if m != nil { @@ -872,7 +942,7 @@ type ClientNote struct { func (m *ClientNote) Reset() { *m = ClientNote{} } func (m *ClientNote) String() string { return proto.CompactTextString(m) } func (*ClientNote) ProtoMessage() {} -func (*ClientNote) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} } +func (*ClientNote) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} } func (m *ClientNote) GetTopic() string { if m != nil { @@ -913,7 +983,7 @@ type ClientMsg struct { func (m *ClientMsg) Reset() { *m = ClientMsg{} } func (m *ClientMsg) String() string { return proto.CompactTextString(m) } func (*ClientMsg) ProtoMessage() {} -func (*ClientMsg) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} } +func (*ClientMsg) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19} } type isClientMsg_Message interface { isClientMsg_Message() @@ -1281,7 +1351,7 @@ type TopicDesc struct { func (m *TopicDesc) Reset() { *m = TopicDesc{} } func (m *TopicDesc) String() string { return proto.CompactTextString(m) } func (*TopicDesc) ProtoMessage() {} -func (*TopicDesc) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19} } +func (*TopicDesc) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20} } func (m *TopicDesc) GetCreatedAt() int64 { if m != nil { @@ -1379,7 +1449,7 @@ type TopicSub struct { func (m *TopicSub) Reset() { *m = TopicSub{} } func (m *TopicSub) String() string { return proto.CompactTextString(m) } func (*TopicSub) ProtoMessage() {} -func (*TopicSub) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20} } +func (*TopicSub) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{21} } func (m *TopicSub) GetUpdatedAt() int64 { if m != nil { @@ -1490,7 +1560,7 @@ type ServerCtrl struct { func (m *ServerCtrl) Reset() { *m = ServerCtrl{} } func (m *ServerCtrl) String() string { return proto.CompactTextString(m) } func (*ServerCtrl) ProtoMessage() {} -func (*ServerCtrl) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{21} } +func (*ServerCtrl) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{22} } func (m *ServerCtrl) GetId() string { if m != nil { @@ -1535,7 +1605,7 @@ type ServerData struct { func (m *ServerData) Reset() { *m = ServerData{} } func (m *ServerData) String() string { return proto.CompactTextString(m) } func (*ServerData) ProtoMessage() {} -func (*ServerData) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{22} } +func (*ServerData) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{23} } func (m *ServerData) GetTopic() string { if m != nil { @@ -1595,7 +1665,7 @@ type ServerPres struct { func (m *ServerPres) Reset() { *m = ServerPres{} } func (m *ServerPres) String() string { return proto.CompactTextString(m) } func (*ServerPres) ProtoMessage() {} -func (*ServerPres) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{23} } +func (*ServerPres) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{24} } func (m *ServerPres) GetTopic() string { if m != nil { @@ -1671,7 +1741,7 @@ type ServerMeta struct { func (m *ServerMeta) Reset() { *m = ServerMeta{} } func (m *ServerMeta) String() string { return proto.CompactTextString(m) } func (*ServerMeta) ProtoMessage() {} -func (*ServerMeta) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{24} } +func (*ServerMeta) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{25} } func (m *ServerMeta) GetId() string { if m != nil { @@ -1712,7 +1782,7 @@ type ServerInfo struct { func (m *ServerInfo) Reset() { *m = ServerInfo{} } func (m *ServerInfo) String() string { return proto.CompactTextString(m) } func (*ServerInfo) ProtoMessage() {} -func (*ServerInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{25} } +func (*ServerInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{26} } func (m *ServerInfo) GetTopic() string { if m != nil { @@ -1756,7 +1826,7 @@ type ServerMsg struct { func (m *ServerMsg) Reset() { *m = ServerMsg{} } func (m *ServerMsg) String() string { return proto.CompactTextString(m) } func (*ServerMsg) ProtoMessage() {} -func (*ServerMsg) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{26} } +func (*ServerMsg) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{27} } type isServerMsg_Message interface { isServerMsg_Message() @@ -1957,6 +2027,39 @@ func _ServerMsg_OneofSizer(msg proto.Message) (n int) { return n } +type ServerResp struct { + Status ServerResp_RespCode `protobuf:"varint,1,opt,name=status,enum=pbx.ServerResp_RespCode" json:"status,omitempty"` + Srvmsg *ServerMsg `protobuf:"bytes,2,opt,name=srvmsg" json:"srvmsg,omitempty"` + Clmsg *ClientMsg `protobuf:"bytes,3,opt,name=clmsg" json:"clmsg,omitempty"` +} + +func (m *ServerResp) Reset() { *m = ServerResp{} } +func (m *ServerResp) String() string { return proto.CompactTextString(m) } +func (*ServerResp) ProtoMessage() {} +func (*ServerResp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{28} } + +func (m *ServerResp) GetStatus() ServerResp_RespCode { + if m != nil { + return m.Status + } + return ServerResp_CONTINUE +} + +func (m *ServerResp) GetSrvmsg() *ServerMsg { + if m != nil { + return m.Srvmsg + } + return nil +} + +func (m *ServerResp) GetClmsg() *ClientMsg { + if m != nil { + return m.Clmsg + } + return nil +} + +// Context message type Session struct { SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId" json:"session_id,omitempty"` UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId" json:"user_id,omitempty"` @@ -1970,7 +2073,7 @@ type Session struct { func (m *Session) Reset() { *m = Session{} } func (m *Session) String() string { return proto.CompactTextString(m) } func (*Session) ProtoMessage() {} -func (*Session) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{27} } +func (*Session) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{29} } func (m *Session) GetSessionId() string { if m != nil { @@ -2021,119 +2124,217 @@ func (m *Session) GetLanguage() string { return "" } -type Topic struct { - Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - AccessAuth string `protobuf:"bytes,2,opt,name=access_auth,json=accessAuth" json:"access_auth,omitempty"` - AccessAnon string `protobuf:"bytes,3,opt,name=access_anon,json=accessAnon" json:"access_anon,omitempty"` - Public []byte `protobuf:"bytes,4,opt,name=public,proto3" json:"public,omitempty"` - Private []byte `protobuf:"bytes,5,opt,name=private,proto3" json:"private,omitempty"` +type ClientReq struct { + Msg *ClientMsg `protobuf:"bytes,1,opt,name=msg" json:"msg,omitempty"` + Sess *Session `protobuf:"bytes,2,opt,name=sess" json:"sess,omitempty"` } -func (m *Topic) Reset() { *m = Topic{} } -func (m *Topic) String() string { return proto.CompactTextString(m) } -func (*Topic) ProtoMessage() {} -func (*Topic) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{28} } +func (m *ClientReq) Reset() { *m = ClientReq{} } +func (m *ClientReq) String() string { return proto.CompactTextString(m) } +func (*ClientReq) ProtoMessage() {} +func (*ClientReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{30} } -func (m *Topic) GetName() string { +func (m *ClientReq) GetMsg() *ClientMsg { + if m != nil { + return m.Msg + } + return nil +} + +func (m *ClientReq) GetSess() *Session { + if m != nil { + return m.Sess + } + return nil +} + +type TopicEvent struct { + Action Crud `protobuf:"varint,1,opt,name=action,enum=pbx.Crud" json:"action,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"` + Desc *TopicDesc `protobuf:"bytes,3,opt,name=desc" json:"desc,omitempty"` +} + +func (m *TopicEvent) Reset() { *m = TopicEvent{} } +func (m *TopicEvent) String() string { return proto.CompactTextString(m) } +func (*TopicEvent) ProtoMessage() {} +func (*TopicEvent) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{31} } + +func (m *TopicEvent) GetAction() Crud { + if m != nil { + return m.Action + } + return Crud_CREATE +} + +func (m *TopicEvent) GetName() string { if m != nil { return m.Name } return "" } -func (m *Topic) GetAccessAuth() string { +func (m *TopicEvent) GetDesc() *TopicDesc { if m != nil { - return m.AccessAuth + return m.Desc } - return "" + return nil +} + +type AccountEvent struct { + Action Crud `protobuf:"varint,1,opt,name=action,enum=pbx.Crud" json:"action,omitempty"` + UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId" json:"user_id,omitempty"` + DefaultAcs *DefaultAcsMode `protobuf:"bytes,3,opt,name=default_acs,json=defaultAcs" json:"default_acs,omitempty"` + Public []byte `protobuf:"bytes,4,opt,name=public,proto3" json:"public,omitempty"` + Private []byte `protobuf:"bytes,5,opt,name=private,proto3" json:"private,omitempty"` + LastSeen int64 `protobuf:"varint,6,opt,name=last_seen,json=lastSeen" json:"last_seen,omitempty"` + UserAgent string `protobuf:"bytes,7,opt,name=user_agent,json=userAgent" json:"user_agent,omitempty"` + AuthSchemes []*AccountEvent_AuthScheme `protobuf:"bytes,8,rep,name=auth_schemes,json=authSchemes" json:"auth_schemes,omitempty"` + // Indexable tags for user discovery + Tags []string `protobuf:"bytes,9,rep,name=tags" json:"tags,omitempty"` +} + +func (m *AccountEvent) Reset() { *m = AccountEvent{} } +func (m *AccountEvent) String() string { return proto.CompactTextString(m) } +func (*AccountEvent) ProtoMessage() {} +func (*AccountEvent) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{32} } + +func (m *AccountEvent) GetAction() Crud { + if m != nil { + return m.Action + } + return Crud_CREATE } -func (m *Topic) GetAccessAnon() string { +func (m *AccountEvent) GetUserId() string { if m != nil { - return m.AccessAnon + return m.UserId } return "" } -func (m *Topic) GetPublic() []byte { +func (m *AccountEvent) GetDefaultAcs() *DefaultAcsMode { + if m != nil { + return m.DefaultAcs + } + return nil +} + +func (m *AccountEvent) GetPublic() []byte { if m != nil { return m.Public } return nil } -func (m *Topic) GetPrivate() []byte { +func (m *AccountEvent) GetPrivate() []byte { if m != nil { return m.Private } return nil } -type User struct { - Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` - ModeWant string `protobuf:"bytes,2,opt,name=mode_want,json=modeWant" json:"mode_want,omitempty"` - ModeGiven string `protobuf:"bytes,3,opt,name=mode_given,json=modeGiven" json:"mode_given,omitempty"` - Public []byte `protobuf:"bytes,4,opt,name=public,proto3" json:"public,omitempty"` +func (m *AccountEvent) GetLastSeen() int64 { + if m != nil { + return m.LastSeen + } + return 0 } -func (m *User) Reset() { *m = User{} } -func (m *User) String() string { return proto.CompactTextString(m) } -func (*User) ProtoMessage() {} -func (*User) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{29} } - -func (m *User) GetId() string { +func (m *AccountEvent) GetUserAgent() string { if m != nil { - return m.Id + return m.UserAgent } return "" } -func (m *User) GetModeWant() string { +func (m *AccountEvent) GetAuthSchemes() []*AccountEvent_AuthScheme { if m != nil { - return m.ModeWant + return m.AuthSchemes } - return "" + return nil } -func (m *User) GetModeGiven() string { +func (m *AccountEvent) GetTags() []string { if m != nil { - return m.ModeGiven + return m.Tags + } + return nil +} + +type AccountEvent_AuthScheme struct { + Scheme string `protobuf:"bytes,1,opt,name=scheme" json:"scheme,omitempty"` + Secret []byte `protobuf:"bytes,2,opt,name=secret,proto3" json:"secret,omitempty"` +} + +func (m *AccountEvent_AuthScheme) Reset() { *m = AccountEvent_AuthScheme{} } +func (m *AccountEvent_AuthScheme) String() string { return proto.CompactTextString(m) } +func (*AccountEvent_AuthScheme) ProtoMessage() {} +func (*AccountEvent_AuthScheme) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{32, 0} } + +func (m *AccountEvent_AuthScheme) GetScheme() string { + if m != nil { + return m.Scheme } return "" } -func (m *User) GetPublic() []byte { +func (m *AccountEvent_AuthScheme) GetSecret() []byte { if m != nil { - return m.Public + return m.Secret } return nil } -type ClientReq struct { - Msg *ClientMsg `protobuf:"bytes,1,opt,name=msg" json:"msg,omitempty"` - Sess *Session `protobuf:"bytes,2,opt,name=sess" json:"sess,omitempty"` +type SubscriptionEvent struct { + Action Crud `protobuf:"varint,1,opt,name=action,enum=pbx.Crud" json:"action,omitempty"` + SubId string `protobuf:"bytes,2,opt,name=sub_id,json=subId" json:"sub_id,omitempty"` + Sub *TopicSub `protobuf:"bytes,3,opt,name=sub" json:"sub,omitempty"` } -func (m *ClientReq) Reset() { *m = ClientReq{} } -func (m *ClientReq) String() string { return proto.CompactTextString(m) } -func (*ClientReq) ProtoMessage() {} -func (*ClientReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{30} } +func (m *SubscriptionEvent) Reset() { *m = SubscriptionEvent{} } +func (m *SubscriptionEvent) String() string { return proto.CompactTextString(m) } +func (*SubscriptionEvent) ProtoMessage() {} +func (*SubscriptionEvent) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{33} } -func (m *ClientReq) GetMsg() *ClientMsg { +func (m *SubscriptionEvent) GetAction() Crud { if m != nil { - return m.Msg + return m.Action } - return nil + return Crud_CREATE } -func (m *ClientReq) GetSess() *Session { +func (m *SubscriptionEvent) GetSubId() string { if m != nil { - return m.Sess + return m.SubId + } + return "" +} + +func (m *SubscriptionEvent) GetSub() *TopicSub { + if m != nil { + return m.Sub } return nil } +type MessageEvent struct { + Action Crud `protobuf:"varint,1,opt,name=action,enum=pbx.Crud" json:"action,omitempty"` +} + +func (m *MessageEvent) Reset() { *m = MessageEvent{} } +func (m *MessageEvent) String() string { return proto.CompactTextString(m) } +func (*MessageEvent) ProtoMessage() {} +func (*MessageEvent) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{34} } + +func (m *MessageEvent) GetAction() Crud { + if m != nil { + return m.Action + } + return Crud_CREATE +} + func init() { + proto.RegisterType((*Unused)(nil), "pbx.Unused") proto.RegisterType((*DefaultAcsMode)(nil), "pbx.DefaultAcsMode") proto.RegisterType((*AccessMode)(nil), "pbx.AccessMode") proto.RegisterType((*SetSub)(nil), "pbx.SetSub") @@ -2161,13 +2362,19 @@ func init() { proto.RegisterType((*ServerMeta)(nil), "pbx.ServerMeta") proto.RegisterType((*ServerInfo)(nil), "pbx.ServerInfo") proto.RegisterType((*ServerMsg)(nil), "pbx.ServerMsg") + proto.RegisterType((*ServerResp)(nil), "pbx.ServerResp") proto.RegisterType((*Session)(nil), "pbx.Session") - proto.RegisterType((*Topic)(nil), "pbx.Topic") - proto.RegisterType((*User)(nil), "pbx.User") proto.RegisterType((*ClientReq)(nil), "pbx.ClientReq") + proto.RegisterType((*TopicEvent)(nil), "pbx.TopicEvent") + proto.RegisterType((*AccountEvent)(nil), "pbx.AccountEvent") + proto.RegisterType((*AccountEvent_AuthScheme)(nil), "pbx.AccountEvent.AuthScheme") + proto.RegisterType((*SubscriptionEvent)(nil), "pbx.SubscriptionEvent") + proto.RegisterType((*MessageEvent)(nil), "pbx.MessageEvent") proto.RegisterEnum("pbx.InfoNote", InfoNote_name, InfoNote_value) + proto.RegisterEnum("pbx.Crud", Crud_name, Crud_value) proto.RegisterEnum("pbx.ClientDel_What", ClientDel_What_name, ClientDel_What_value) proto.RegisterEnum("pbx.ServerPres_What", ServerPres_What_name, ServerPres_What_value) + proto.RegisterEnum("pbx.ServerResp_RespCode", ServerResp_RespCode_name, ServerResp_RespCode_value) proto.RegisterEnum("pbx.Session_AuthLevel", Session_AuthLevel_name, Session_AuthLevel_value) } @@ -2280,14 +2487,20 @@ var _Node_serviceDesc = grpc.ServiceDesc{ // Client API for Plugin service type PluginClient interface { - // This plugin method is called for every message received from the client. The method returns - // a ServerCtrl message. ServerCtrl.code is not 0 indicates that no further processing is needed. Server - // will generate a {ctrl} message from ServerCtrl and forward it to the client session. - // If ServerCtrl.code is 0, the server should continue with default processing of the message. - ClientMessage(ctx context.Context, in *ClientReq, opts ...grpc.CallOption) (*ServerCtrl, error) - // This method is called immmediately before a server message is broadcasted to topic subscribers. - // The filter may alter the server message or may request to drop it. - ServerMessage(ctx context.Context, in *ServerMsg, opts ...grpc.CallOption) (*ServerMsg, error) + // This plugin method is called by Tinode server for every message received from the clients. The + // method returns a ServerCtrl message. ServerCtrl.code is *not* 0 indicates that no further + // processing is needed. The Tinode server will generate a {ctrl} message from the returned ServerCtrl + // and forward it to the client session. + // If ServerCtrl.code is 0, the server should continue with default processing of the client message. + FireHose(ctx context.Context, in *ClientReq, opts ...grpc.CallOption) (*ServerResp, error) + // Account created, updated or deleted + Account(ctx context.Context, in *AccountEvent, opts ...grpc.CallOption) (*Unused, error) + // Topic created, updated [or deleted -- not supported yet] + Topic(ctx context.Context, in *TopicEvent, opts ...grpc.CallOption) (*Unused, error) + // Subscription created, updated or deleted + Subscription(ctx context.Context, in *SubscriptionEvent, opts ...grpc.CallOption) (*Unused, error) + // Message published or deleted + Message(ctx context.Context, in *MessageEvent, opts ...grpc.CallOption) (*Unused, error) } type pluginClient struct { @@ -2298,18 +2511,45 @@ func NewPluginClient(cc *grpc.ClientConn) PluginClient { return &pluginClient{cc} } -func (c *pluginClient) ClientMessage(ctx context.Context, in *ClientReq, opts ...grpc.CallOption) (*ServerCtrl, error) { - out := new(ServerCtrl) - err := grpc.Invoke(ctx, "/pbx.Plugin/ClientMessage", in, out, c.cc, opts...) +func (c *pluginClient) FireHose(ctx context.Context, in *ClientReq, opts ...grpc.CallOption) (*ServerResp, error) { + out := new(ServerResp) + err := grpc.Invoke(ctx, "/pbx.Plugin/FireHose", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *pluginClient) Account(ctx context.Context, in *AccountEvent, opts ...grpc.CallOption) (*Unused, error) { + out := new(Unused) + err := grpc.Invoke(ctx, "/pbx.Plugin/Account", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *pluginClient) Topic(ctx context.Context, in *TopicEvent, opts ...grpc.CallOption) (*Unused, error) { + out := new(Unused) + err := grpc.Invoke(ctx, "/pbx.Plugin/Topic", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *pluginClient) Subscription(ctx context.Context, in *SubscriptionEvent, opts ...grpc.CallOption) (*Unused, error) { + out := new(Unused) + err := grpc.Invoke(ctx, "/pbx.Plugin/Subscription", in, out, c.cc, opts...) if err != nil { return nil, err } return out, nil } -func (c *pluginClient) ServerMessage(ctx context.Context, in *ServerMsg, opts ...grpc.CallOption) (*ServerMsg, error) { - out := new(ServerMsg) - err := grpc.Invoke(ctx, "/pbx.Plugin/ServerMessage", in, out, c.cc, opts...) +func (c *pluginClient) Message(ctx context.Context, in *MessageEvent, opts ...grpc.CallOption) (*Unused, error) { + out := new(Unused) + err := grpc.Invoke(ctx, "/pbx.Plugin/Message", in, out, c.cc, opts...) if err != nil { return nil, err } @@ -2319,52 +2559,112 @@ func (c *pluginClient) ServerMessage(ctx context.Context, in *ServerMsg, opts .. // Server API for Plugin service type PluginServer interface { - // This plugin method is called for every message received from the client. The method returns - // a ServerCtrl message. ServerCtrl.code is not 0 indicates that no further processing is needed. Server - // will generate a {ctrl} message from ServerCtrl and forward it to the client session. - // If ServerCtrl.code is 0, the server should continue with default processing of the message. - ClientMessage(context.Context, *ClientReq) (*ServerCtrl, error) - // This method is called immmediately before a server message is broadcasted to topic subscribers. - // The filter may alter the server message or may request to drop it. - ServerMessage(context.Context, *ServerMsg) (*ServerMsg, error) + // This plugin method is called by Tinode server for every message received from the clients. The + // method returns a ServerCtrl message. ServerCtrl.code is *not* 0 indicates that no further + // processing is needed. The Tinode server will generate a {ctrl} message from the returned ServerCtrl + // and forward it to the client session. + // If ServerCtrl.code is 0, the server should continue with default processing of the client message. + FireHose(context.Context, *ClientReq) (*ServerResp, error) + // Account created, updated or deleted + Account(context.Context, *AccountEvent) (*Unused, error) + // Topic created, updated [or deleted -- not supported yet] + Topic(context.Context, *TopicEvent) (*Unused, error) + // Subscription created, updated or deleted + Subscription(context.Context, *SubscriptionEvent) (*Unused, error) + // Message published or deleted + Message(context.Context, *MessageEvent) (*Unused, error) } func RegisterPluginServer(s *grpc.Server, srv PluginServer) { s.RegisterService(&_Plugin_serviceDesc, srv) } -func _Plugin_ClientMessage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { +func _Plugin_FireHose_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ClientReq) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(PluginServer).ClientMessage(ctx, in) + return srv.(PluginServer).FireHose(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pbx.Plugin/FireHose", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PluginServer).FireHose(ctx, req.(*ClientReq)) + } + return interceptor(ctx, in, info, handler) +} + +func _Plugin_Account_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AccountEvent) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PluginServer).Account(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pbx.Plugin/Account", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PluginServer).Account(ctx, req.(*AccountEvent)) + } + return interceptor(ctx, in, info, handler) +} + +func _Plugin_Topic_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TopicEvent) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PluginServer).Topic(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pbx.Plugin/Topic", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PluginServer).Topic(ctx, req.(*TopicEvent)) + } + return interceptor(ctx, in, info, handler) +} + +func _Plugin_Subscription_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SubscriptionEvent) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PluginServer).Subscription(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/pbx.Plugin/ClientMessage", + FullMethod: "/pbx.Plugin/Subscription", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PluginServer).ClientMessage(ctx, req.(*ClientReq)) + return srv.(PluginServer).Subscription(ctx, req.(*SubscriptionEvent)) } return interceptor(ctx, in, info, handler) } -func _Plugin_ServerMessage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ServerMsg) +func _Plugin_Message_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MessageEvent) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(PluginServer).ServerMessage(ctx, in) + return srv.(PluginServer).Message(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/pbx.Plugin/ServerMessage", + FullMethod: "/pbx.Plugin/Message", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PluginServer).ServerMessage(ctx, req.(*ServerMsg)) + return srv.(PluginServer).Message(ctx, req.(*MessageEvent)) } return interceptor(ctx, in, info, handler) } @@ -2374,12 +2674,24 @@ var _Plugin_serviceDesc = grpc.ServiceDesc{ HandlerType: (*PluginServer)(nil), Methods: []grpc.MethodDesc{ { - MethodName: "ClientMessage", - Handler: _Plugin_ClientMessage_Handler, + MethodName: "FireHose", + Handler: _Plugin_FireHose_Handler, + }, + { + MethodName: "Account", + Handler: _Plugin_Account_Handler, + }, + { + MethodName: "Topic", + Handler: _Plugin_Topic_Handler, + }, + { + MethodName: "Subscription", + Handler: _Plugin_Subscription_Handler, }, { - MethodName: "ServerMessage", - Handler: _Plugin_ServerMessage_Handler, + MethodName: "Message", + Handler: _Plugin_Message_Handler, }, }, Streams: []grpc.StreamDesc{}, @@ -2389,133 +2701,149 @@ var _Plugin_serviceDesc = grpc.ServiceDesc{ func init() { proto.RegisterFile("model.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 2035 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x18, 0xcb, 0x72, 0xe3, 0x58, - 0xd5, 0xb6, 0x6c, 0x59, 0x3a, 0x4e, 0xd2, 0x1a, 0x11, 0x7a, 0x3c, 0xe9, 0xea, 0xe9, 0xb4, 0x7a, - 0x80, 0x54, 0x03, 0x01, 0xd2, 0x0c, 0x0c, 0xb0, 0x72, 0x27, 0x99, 0x24, 0xd5, 0x79, 0x21, 0x27, - 0x33, 0x1b, 0xaa, 0x5c, 0xb2, 0x74, 0x62, 0x8b, 0x91, 0x25, 0x47, 0xf7, 0xda, 0xdd, 0x5d, 0x45, - 0xb1, 0x63, 0x31, 0x6c, 0xa8, 0xe2, 0x07, 0xd8, 0xb1, 0x85, 0x05, 0x7f, 0xc1, 0x17, 0xb0, 0xe2, - 0x47, 0x58, 0x50, 0xe7, 0x3e, 0x64, 0xc9, 0xc1, 0xdd, 0x29, 0x60, 0x77, 0xef, 0x39, 0xe7, 0x9e, - 0xf7, 0x4b, 0x82, 0xce, 0x24, 0x8b, 0x30, 0xd9, 0x9d, 0xe6, 0x19, 0xcf, 0x5c, 0x63, 0x3a, 0x7c, - 0xe3, 0x7d, 0x06, 0x1b, 0x07, 0x78, 0x13, 0xcc, 0x12, 0xde, 0x0b, 0xd9, 0x59, 0x16, 0xa1, 0xeb, - 0x42, 0x33, 0x98, 0xf1, 0x71, 0xb7, 0xbe, 0x5d, 0xdf, 0xb1, 0x7d, 0x71, 0x16, 0xb0, 0x34, 0x4b, - 0xbb, 0x0d, 0x05, 0x4b, 0xb3, 0xd4, 0xfb, 0x09, 0x40, 0x2f, 0x0c, 0x91, 0x15, 0xaf, 0x5e, 0x07, - 0x29, 0xd7, 0xaf, 0xe8, 0xec, 0x6e, 0x42, 0x6b, 0x14, 0xcf, 0x51, 0x3f, 0x93, 0x17, 0xef, 0x53, - 0x30, 0xfb, 0xc8, 0xfb, 0xb3, 0xa1, 0xfb, 0x21, 0xb4, 0x67, 0x0c, 0xf3, 0x41, 0x1c, 0xa9, 0x67, - 0x26, 0x5d, 0x4f, 0x22, 0x62, 0x46, 0x8a, 0x6a, 0x71, 0x74, 0xf6, 0x6e, 0xa1, 0xdd, 0x47, 0x7e, - 0x80, 0x2c, 0x74, 0x7f, 0x0c, 0x9d, 0x48, 0xea, 0x3c, 0x08, 0x42, 0x26, 0xde, 0x76, 0xf6, 0xbe, - 0xb1, 0x3b, 0x1d, 0xbe, 0xd9, 0xad, 0xda, 0xe2, 0x43, 0x54, 0xdc, 0xdd, 0x87, 0x60, 0x4e, 0x67, - 0xc3, 0x24, 0x0e, 0x05, 0xdb, 0x35, 0x5f, 0xdd, 0xdc, 0x2e, 0xb4, 0xa7, 0x79, 0x3c, 0x0f, 0x38, - 0x76, 0x0d, 0x81, 0xd0, 0x57, 0xef, 0x15, 0xb4, 0x8f, 0x90, 0x5f, 0x4c, 0x39, 0x73, 0x9f, 0xc3, - 0x07, 0xf1, 0xcd, 0x60, 0x92, 0x45, 0xf1, 0x4d, 0x8c, 0xd1, 0x80, 0xc5, 0x69, 0x88, 0x42, 0xb0, - 0xe1, 0x3f, 0x88, 0x6f, 0xce, 0x14, 0xbc, 0x4f, 0x60, 0x32, 0x3b, 0x89, 0x27, 0x31, 0x17, 0x72, - 0x5a, 0xbe, 0xbc, 0x78, 0x7f, 0xac, 0x03, 0xbc, 0xcc, 0xb3, 0xd7, 0x0c, 0x05, 0xc3, 0x8f, 0xc0, - 0x12, 0x4c, 0xb4, 0xf1, 0x2d, 0xbf, 0x2d, 0xee, 0x27, 0xd1, 0x02, 0xc5, 0x99, 0x60, 0x61, 0x28, - 0xd4, 0x15, 0x73, 0x1f, 0x81, 0x3d, 0xc4, 0x9b, 0x2c, 0x17, 0xcf, 0x0c, 0xf1, 0xcc, 0x92, 0x80, - 0x93, 0xa8, 0x84, 0xe4, 0xac, 0xdb, 0x14, 0x0f, 0x15, 0xf2, 0x8a, 0x2d, 0x94, 0x6a, 0x95, 0x95, - 0xfa, 0x5d, 0x1d, 0xac, 0x23, 0xe4, 0xbf, 0x9c, 0x61, 0xfe, 0x56, 0x84, 0x70, 0x1c, 0x2c, 0x42, - 0x38, 0x0e, 0xb8, 0xbb, 0x0d, 0xcd, 0x08, 0x99, 0x74, 0x59, 0x67, 0x6f, 0x4d, 0xf8, 0x58, 0xf9, - 0xc4, 0x17, 0x18, 0xf7, 0x63, 0x30, 0xd8, 0x6c, 0x28, 0x94, 0x59, 0x26, 0x20, 0x84, 0xfb, 0x0c, - 0x9a, 0x51, 0xc0, 0x03, 0xa1, 0x50, 0x67, 0xef, 0x81, 0x20, 0x58, 0xf8, 0xc1, 0x17, 0x48, 0xef, - 0x15, 0x58, 0x7d, 0xad, 0x86, 0x16, 0x59, 0x2f, 0x71, 0x54, 0x91, 0x57, 0x22, 0x1f, 0x4b, 0x91, - 0x52, 0xa7, 0x8e, 0x26, 0xe8, 0xcf, 0x86, 0x42, 0xa2, 0xf7, 0x1b, 0xb0, 0xf6, 0x93, 0x18, 0x53, - 0x7e, 0x1c, 0xbb, 0x1b, 0xd0, 0x28, 0xb2, 0xab, 0x11, 0x47, 0xee, 0x63, 0x00, 0x91, 0x72, 0xc1, - 0x08, 0x53, 0xae, 0xf2, 0xcb, 0x26, 0x48, 0x8f, 0x00, 0xae, 0x03, 0xc6, 0x1c, 0x73, 0xe5, 0x59, - 0x3a, 0x92, 0x53, 0x23, 0x9c, 0xc7, 0x32, 0x50, 0x4d, 0x41, 0x6f, 0x49, 0x80, 0xcc, 0xd3, 0x24, - 0x48, 0x47, 0xc2, 0xa7, 0xb6, 0x2f, 0xce, 0xde, 0x5f, 0xeb, 0x60, 0x4b, 0xf1, 0xbd, 0x30, 0xbc, - 0x23, 0xbf, 0x94, 0xf2, 0x8d, 0x4a, 0xca, 0x3f, 0x04, 0x93, 0x85, 0x63, 0x9c, 0xc8, 0x24, 0xb4, - 0x7d, 0x75, 0x13, 0x70, 0x0c, 0x73, 0xe4, 0x42, 0xf8, 0x9a, 0xaf, 0x6e, 0x22, 0x9e, 0xd9, 0x28, - 0x4e, 0x85, 0x6c, 0xcb, 0x97, 0x17, 0x52, 0x88, 0x07, 0x23, 0xd6, 0x35, 0xb7, 0x0d, 0x52, 0x88, - 0xce, 0x85, 0x3f, 0xdb, 0xab, 0xfc, 0xe9, 0x9d, 0x41, 0x47, 0x6a, 0x7c, 0x2a, 0x98, 0x2c, 0xeb, - 0xbc, 0x50, 0xad, 0xb1, 0x42, 0x35, 0xa3, 0xac, 0x9a, 0xf7, 0x75, 0xe1, 0x01, 0x2a, 0xf2, 0x65, - 0x6e, 0x9b, 0xd0, 0xe2, 0xd9, 0x54, 0x55, 0xa1, 0xed, 0xcb, 0x8b, 0xfb, 0x1c, 0x6c, 0x86, 0x7c, - 0x70, 0x4b, 0x19, 0xa0, 0x72, 0x69, 0x5d, 0x6b, 0x2a, 0xd2, 0xc2, 0xb7, 0x98, 0x4e, 0x90, 0xe7, - 0x60, 0x8f, 0x0a, 0xda, 0x66, 0x89, 0xf6, 0xa8, 0xa0, 0x1d, 0xa9, 0x93, 0x77, 0x52, 0x98, 0x86, - 0xc1, 0x1c, 0xef, 0xa9, 0xcc, 0x26, 0xb4, 0x66, 0xa9, 0x4e, 0x6a, 0xcb, 0x97, 0x17, 0xef, 0xef, - 0x85, 0x59, 0x97, 0xf7, 0x36, 0xeb, 0x43, 0x68, 0xa7, 0xd9, 0x00, 0xc3, 0x71, 0xa6, 0x78, 0x99, - 0x69, 0x76, 0x18, 0x8e, 0x33, 0xf7, 0x7b, 0xd0, 0x1c, 0x63, 0x40, 0x19, 0x65, 0xec, 0x74, 0xf6, - 0xba, 0x42, 0xfd, 0x82, 0xf9, 0xee, 0x31, 0x06, 0xd1, 0x61, 0xca, 0xf3, 0xb7, 0xbe, 0xa0, 0xa2, - 0x16, 0x15, 0x66, 0x29, 0xa7, 0x94, 0x6d, 0xc9, 0x16, 0xa5, 0xae, 0x5b, 0x3f, 0x05, 0xbb, 0x20, - 0xa6, 0xec, 0xfd, 0x0a, 0xdf, 0x2a, 0xa5, 0xe8, 0x48, 0x5a, 0xcd, 0x83, 0x64, 0xa6, 0x23, 0x27, - 0x2f, 0x3f, 0x6f, 0x7c, 0x56, 0xf7, 0xbe, 0xd0, 0xc6, 0x1c, 0x21, 0xbf, 0xa7, 0x31, 0xcf, 0xa0, - 0x75, 0x37, 0x3e, 0x85, 0xcf, 0x25, 0x6e, 0xc1, 0xb7, 0xff, 0xbf, 0xf1, 0xed, 0x2f, 0xf1, 0xfd, - 0x67, 0xe1, 0xfd, 0x03, 0x4c, 0xee, 0xc9, 0xf8, 0x3b, 0xaa, 0xa1, 0x11, 0xdf, 0x0d, 0x35, 0x20, - 0x0a, 0x1e, 0xbb, 0x5f, 0x8e, 0x03, 0xae, 0xba, 0xdc, 0x43, 0x30, 0x65, 0xa3, 0x14, 0xe9, 0xd4, - 0xf2, 0xd5, 0x4d, 0x74, 0x62, 0xbc, 0x1d, 0x24, 0x31, 0x23, 0xc7, 0x1b, 0xa2, 0x49, 0xe3, 0xed, - 0x69, 0xcc, 0x78, 0xb9, 0x90, 0xcd, 0xe5, 0xd9, 0x35, 0x0e, 0xf2, 0x48, 0x94, 0x9b, 0xe5, 0x8b, - 0xb3, 0xf7, 0x0c, 0x9a, 0x24, 0xcd, 0x6d, 0x83, 0x71, 0xd6, 0x3f, 0x72, 0x6a, 0xae, 0x0d, 0xad, - 0xab, 0x8b, 0xcb, 0x93, 0x7d, 0xa7, 0x4e, 0xb0, 0xfe, 0xf5, 0x4b, 0xa7, 0xe1, 0xfd, 0x0a, 0x40, - 0x2a, 0x77, 0x9e, 0x71, 0x5c, 0x58, 0x54, 0x2f, 0x5b, 0xf4, 0x54, 0x59, 0xd4, 0x10, 0x16, 0x49, - 0x4f, 0x9d, 0xa4, 0x37, 0x19, 0x3d, 0x51, 0xb6, 0x7c, 0x93, 0xaa, 0xf2, 0x76, 0x31, 0x1f, 0x5a, - 0x0c, 0x6f, 0x4f, 0x22, 0xef, 0xf7, 0x86, 0xf6, 0xdf, 0x19, 0x1b, 0xb9, 0x4f, 0xa0, 0x31, 0x8e, - 0x55, 0x87, 0x5d, 0x2f, 0xf9, 0xe5, 0x38, 0x3e, 0xae, 0xf9, 0x8d, 0x71, 0xec, 0x7a, 0x60, 0x04, - 0xa1, 0x6e, 0xfb, 0x1b, 0x25, 0x8a, 0x5e, 0x18, 0x1e, 0xd7, 0x7c, 0x42, 0xba, 0x3b, 0xba, 0x05, - 0xc9, 0xb8, 0x39, 0x25, 0x2a, 0xd1, 0x48, 0x8e, 0x6b, 0xba, 0x2d, 0x79, 0xb2, 0x61, 0x37, 0xef, - 0x70, 0xeb, 0xcf, 0x86, 0xc4, 0x8d, 0xe6, 0x04, 0x71, 0xa3, 0x1a, 0x15, 0x19, 0xbe, 0xc4, 0x8d, - 0xe0, 0x82, 0x9b, 0x28, 0x62, 0x0f, 0x8c, 0xe9, 0x6c, 0x28, 0xdc, 0x5e, 0xe5, 0x76, 0x29, 0xb9, - 0x4d, 0x67, 0x43, 0xa2, 0x19, 0x21, 0x57, 0x3d, 0xaf, 0x4c, 0x73, 0x84, 0x9c, 0x68, 0x46, 0xc8, - 0x85, 0x56, 0xc8, 0xbb, 0xd6, 0x5d, 0xad, 0x24, 0x0d, 0x93, 0x34, 0x11, 0x26, 0x5d, 0xfb, 0x0e, - 0xcd, 0x01, 0x26, 0x44, 0x13, 0x61, 0xe2, 0x7e, 0x0b, 0x9a, 0x69, 0xc6, 0xb1, 0x0b, 0xa5, 0x09, - 0xb7, 0x88, 0xe4, 0x71, 0xcd, 0x17, 0xe8, 0x97, 0x36, 0xb4, 0xcf, 0x90, 0xb1, 0x60, 0x84, 0xde, - 0x5f, 0x1a, 0x60, 0x5f, 0x51, 0x40, 0x0f, 0xe4, 0x38, 0x83, 0x30, 0xc7, 0x80, 0x63, 0x34, 0x50, - 0xd3, 0xd7, 0xf0, 0x6d, 0x05, 0xe9, 0x71, 0x31, 0xb2, 0xa6, 0x91, 0x46, 0xcb, 0x85, 0xc0, 0x56, - 0x90, 0x1e, 0x77, 0xbf, 0x0b, 0x26, 0x2d, 0x39, 0x21, 0x53, 0xee, 0xfd, 0x8f, 0x7b, 0x90, 0x22, - 0x71, 0x9f, 0x52, 0x58, 0x99, 0x72, 0xb1, 0xd4, 0x74, 0xb1, 0xc3, 0x51, 0x54, 0x59, 0x29, 0x7f, - 0xcc, 0x52, 0xfe, 0x50, 0xbe, 0xe7, 0x18, 0x44, 0x04, 0x6f, 0xcb, 0x1a, 0xa1, 0xab, 0x46, 0x84, - 0x73, 0x42, 0x58, 0x1a, 0x11, 0xce, 0xe5, 0x1a, 0x13, 0x26, 0x18, 0x88, 0x12, 0xb1, 0xe5, 0x86, - 0x23, 0xee, 0x72, 0xd8, 0xa9, 0x55, 0x0c, 0x56, 0xad, 0x62, 0x9d, 0xea, 0x2a, 0xf6, 0x27, 0x03, - 0x2c, 0xe1, 0x31, 0x1a, 0x29, 0x55, 0x8f, 0xd4, 0x97, 0x3d, 0xf2, 0x18, 0x20, 0xc2, 0x04, 0xab, - 0x0e, 0x53, 0x90, 0x9e, 0x28, 0xf6, 0x2c, 0x4d, 0xe2, 0x14, 0x75, 0x4b, 0x96, 0x37, 0xed, 0x9b, - 0xe6, 0x3b, 0x7c, 0x53, 0x72, 0x42, 0x6b, 0x95, 0x13, 0xcc, 0x8a, 0x13, 0x16, 0x96, 0xb6, 0x57, - 0x59, 0x6a, 0x55, 0x2c, 0x2d, 0x37, 0x16, 0xbb, 0xd2, 0x58, 0x8a, 0x8e, 0x00, 0xe5, 0x8e, 0xb0, - 0x08, 0x57, 0xa7, 0x1c, 0xae, 0xb2, 0xf3, 0xd7, 0xaa, 0xce, 0xff, 0x04, 0x36, 0x92, 0x80, 0xf1, - 0x01, 0x43, 0x4c, 0x07, 0x3c, 0x9e, 0x60, 0x77, 0x5d, 0xb8, 0x68, 0x8d, 0xa0, 0x7d, 0xc4, 0xf4, - 0x2a, 0x9e, 0xa0, 0xfb, 0x03, 0xd8, 0x5c, 0x50, 0x95, 0x56, 0xa6, 0x0d, 0x21, 0xfc, 0x03, 0x4d, - 0x7b, 0xad, 0x57, 0x27, 0xef, 0x6f, 0x75, 0x80, 0x3e, 0xe6, 0x73, 0xcc, 0xf7, 0x79, 0x7e, 0xb7, - 0x43, 0xbb, 0xd0, 0x0c, 0xf5, 0x4a, 0xdf, 0xf2, 0xc5, 0x59, 0x6c, 0x2b, 0xf8, 0x86, 0xab, 0x8d, - 0x47, 0x9c, 0xdd, 0x17, 0x60, 0x4e, 0x83, 0x3c, 0x98, 0x30, 0x35, 0x1a, 0x1f, 0xa9, 0x69, 0xa0, - 0x19, 0xef, 0x5e, 0x0a, 0xac, 0x9c, 0x8e, 0x8a, 0x74, 0xeb, 0x67, 0xd0, 0x29, 0x81, 0xdf, 0x37, - 0x07, 0xd7, 0xca, 0x73, 0xf0, 0x5f, 0x85, 0xda, 0x07, 0x01, 0x0f, 0x56, 0xb4, 0xdd, 0x6d, 0x58, - 0xbb, 0xc9, 0xb3, 0xc9, 0xa0, 0xba, 0xba, 0x01, 0xc1, 0xae, 0x65, 0x70, 0xaa, 0x39, 0x67, 0x2c, - 0xe7, 0xdc, 0x22, 0x4a, 0xcd, 0x72, 0x94, 0xbe, 0xaf, 0xb6, 0x80, 0x96, 0x30, 0xf5, 0xa3, 0x92, - 0xa9, 0xa4, 0xcc, 0xbb, 0xd6, 0x00, 0xf3, 0xff, 0xb4, 0x06, 0x7c, 0x6d, 0x68, 0xf3, 0x2f, 0x73, - 0x64, 0x2b, 0xcc, 0x77, 0xc0, 0x60, 0xb9, 0x9e, 0xad, 0x74, 0x74, 0x77, 0x2a, 0x93, 0x75, 0xb3, - 0xa4, 0x38, 0xb1, 0x29, 0x8f, 0xd6, 0xea, 0xc2, 0xdd, 0x5c, 0x5e, 0xb8, 0x17, 0x8e, 0x69, 0x2d, - 0xa5, 0x6f, 0x31, 0x78, 0xcd, 0xea, 0xe0, 0xfd, 0x04, 0x36, 0x78, 0x90, 0xd3, 0x02, 0xa8, 0xa3, - 0xd1, 0x16, 0x4c, 0xd7, 0x24, 0x54, 0xc5, 0xc3, 0x83, 0xf5, 0x20, 0xe4, 0x59, 0x5e, 0x10, 0x59, - 0x82, 0xa8, 0x23, 0x80, 0x8a, 0x46, 0x15, 0xbc, 0xbd, 0xba, 0xe0, 0xbd, 0xaf, 0xd4, 0xe0, 0x36, - 0xa1, 0x71, 0x71, 0xee, 0xd4, 0x68, 0x58, 0x5f, 0x7c, 0xfe, 0xb9, 0x53, 0x27, 0xc0, 0x75, 0xcf, - 0x31, 0x08, 0x70, 0x7d, 0x79, 0xe0, 0x34, 0x5d, 0x0b, 0x9a, 0x47, 0x17, 0xe7, 0x87, 0x4e, 0x8b, - 0x40, 0xbd, 0xfd, 0xbe, 0x63, 0x12, 0xe8, 0xea, 0xd0, 0x3f, 0x73, 0xda, 0x7a, 0xee, 0x5b, 0x04, - 0xf2, 0x0f, 0x7b, 0x07, 0x8e, 0x2d, 0x4f, 0xfb, 0x5f, 0x38, 0x40, 0xc8, 0x83, 0xc3, 0x53, 0xa7, - 0xe3, 0xbd, 0xd6, 0xa1, 0x38, 0x43, 0x1e, 0xdc, 0x73, 0xc5, 0xf1, 0xd4, 0x72, 0x6f, 0x94, 0x06, - 0x54, 0x31, 0x59, 0xd4, 0xe7, 0xd2, 0x13, 0x3d, 0x7d, 0x8d, 0x62, 0xda, 0xeb, 0x56, 0x2a, 0x3f, - 0x98, 0x7e, 0xab, 0x05, 0xd3, 0x2a, 0xf1, 0x5f, 0x97, 0xc0, 0xd3, 0x4a, 0x4e, 0xbc, 0x67, 0x37, - 0x29, 0x97, 0x81, 0xf7, 0x8f, 0x3a, 0xd8, 0xca, 0x72, 0x36, 0xa2, 0x71, 0x1a, 0xf2, 0x3c, 0x51, - 0xdb, 0xc9, 0x83, 0xa5, 0xfa, 0xa7, 0x71, 0x4a, 0x68, 0x22, 0x13, 0xdf, 0x95, 0x8d, 0x3b, 0x64, - 0x54, 0x3b, 0x44, 0x46, 0x68, 0x22, 0x9b, 0xe6, 0xc8, 0x94, 0x83, 0x1e, 0x2c, 0x65, 0x2a, 0x91, - 0x11, 0x9a, 0xc8, 0x26, 0xb8, 0xf4, 0x95, 0xba, 0x08, 0x06, 0x91, 0x11, 0x9a, 0xc8, 0xe2, 0xf4, - 0x26, 0xab, 0x0c, 0xd0, 0x85, 0xeb, 0x88, 0x8c, 0xd0, 0xe5, 0x51, 0xff, 0xe7, 0x06, 0xb4, 0xfb, - 0xc8, 0x58, 0x9c, 0xa5, 0x54, 0x0b, 0x4c, 0x1e, 0x17, 0xbf, 0x3c, 0x6c, 0x05, 0x39, 0x79, 0xc7, - 0xb7, 0xe1, 0xa7, 0x00, 0xc1, 0x8c, 0x8f, 0x07, 0x09, 0xce, 0x31, 0x51, 0xfe, 0x7d, 0xa8, 0x64, - 0x8b, 0xc7, 0xbb, 0xbd, 0x19, 0x1f, 0x9f, 0x12, 0xd6, 0xb7, 0x03, 0x7d, 0x74, 0x9f, 0x40, 0x27, - 0xc7, 0x49, 0xc6, 0x71, 0x10, 0x44, 0x51, 0xae, 0x6a, 0x0f, 0x24, 0xa8, 0x17, 0x45, 0xf9, 0x52, - 0x6d, 0xb6, 0x96, 0x6b, 0xb3, 0xf2, 0xe9, 0x6b, 0x2e, 0x7d, 0xfa, 0x6e, 0x81, 0x45, 0x9f, 0xbb, - 0xb3, 0x60, 0x84, 0xaa, 0x00, 0x8b, 0xbb, 0xf7, 0x02, 0xec, 0x42, 0x21, 0x4a, 0xf4, 0x73, 0x2a, - 0x8c, 0x1a, 0x9d, 0x7a, 0xe7, 0x17, 0xe7, 0x0e, 0x88, 0xd3, 0xf5, 0xd5, 0xb1, 0xb3, 0x29, 0xca, - 0xe0, 0xe2, 0xe2, 0xca, 0xf9, 0xd8, 0xfb, 0x43, 0x1d, 0x5a, 0x22, 0x2d, 0x69, 0x2c, 0xa4, 0xc1, - 0x04, 0xf5, 0x7f, 0x08, 0x3a, 0x93, 0x2d, 0x81, 0xa8, 0xcd, 0x81, 0xf8, 0x37, 0xa5, 0xb2, 0x4f, - 0x82, 0x48, 0x56, 0x99, 0x20, 0xcd, 0x52, 0x35, 0x52, 0x34, 0x41, 0x9a, 0xa5, 0xa5, 0x49, 0xdc, - 0x5c, 0x35, 0x89, 0x5b, 0xd5, 0x9d, 0xe3, 0xd7, 0xd0, 0xa4, 0xd4, 0xbe, 0x53, 0x89, 0x8f, 0xc0, - 0x9e, 0x64, 0x11, 0x0e, 0xc4, 0xff, 0x2e, 0xa9, 0x89, 0x45, 0x80, 0x2f, 0x83, 0x54, 0xf4, 0x3b, - 0x81, 0x94, 0x3f, 0xbe, 0xa4, 0x1a, 0x82, 0xfc, 0x88, 0x00, 0xab, 0xb4, 0xf0, 0x2e, 0xf4, 0x76, - 0xee, 0xe3, 0xad, 0xbb, 0x0d, 0xc6, 0x84, 0x8d, 0x54, 0x01, 0x94, 0x97, 0xce, 0x33, 0x36, 0xf2, - 0x09, 0x45, 0xdf, 0xf4, 0x94, 0x37, 0x95, 0xdf, 0x32, 0x2a, 0x17, 0x7c, 0x81, 0x79, 0xfe, 0x6d, - 0xb0, 0x74, 0xf1, 0x15, 0x5d, 0xa7, 0x56, 0x74, 0x1d, 0xd1, 0xc0, 0x5e, 0x5d, 0x3a, 0x8d, 0xbd, - 0x5f, 0x40, 0xf3, 0x9c, 0x66, 0xf1, 0x0b, 0xe8, 0xa8, 0x94, 0x3d, 0xcd, 0xb2, 0xa9, 0xbb, 0x24, - 0x75, 0x6b, 0xa3, 0x5c, 0x11, 0x6c, 0xe4, 0xd5, 0x76, 0xea, 0x3f, 0xac, 0xef, 0x65, 0x60, 0x5e, - 0x26, 0x33, 0xda, 0xf0, 0xf7, 0x60, 0x5d, 0x11, 0x4b, 0x26, 0x15, 0x06, 0x3e, 0xde, 0x6e, 0x2d, - 0xd7, 0xb1, 0x57, 0x73, 0x7f, 0x04, 0xeb, 0xba, 0xc4, 0xca, 0x6f, 0x0a, 0x21, 0x77, 0x85, 0x0e, - 0x4d, 0xf1, 0xe7, 0xf2, 0xc5, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x64, 0x1f, 0xce, 0xe1, 0xc8, - 0x14, 0x00, 0x00, + // 2304 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x38, 0xcd, 0x72, 0xdb, 0xc8, + 0xd1, 0x22, 0x41, 0x82, 0x40, 0x93, 0x96, 0xe0, 0xf9, 0xf4, 0x79, 0xb9, 0x72, 0xbc, 0xb6, 0x61, + 0x67, 0xd7, 0x25, 0x67, 0x95, 0x8d, 0x1c, 0x27, 0x9b, 0x64, 0xab, 0x52, 0xb4, 0x48, 0x4b, 0x2a, + 0x4b, 0x22, 0x03, 0x4a, 0x9b, 0x4b, 0xaa, 0x58, 0x20, 0x30, 0x22, 0x51, 0x4b, 0x01, 0x14, 0x66, + 0x40, 0xdb, 0x55, 0xa9, 0x54, 0xe5, 0x90, 0xc3, 0xe6, 0x98, 0x17, 0xc8, 0x2d, 0xd7, 0xe4, 0x90, + 0xa7, 0x48, 0x9e, 0x20, 0xa7, 0x3c, 0x43, 0xee, 0x39, 0xa4, 0x7a, 0x66, 0xf0, 0x2b, 0xcb, 0xab, + 0xfc, 0x5c, 0x50, 0x33, 0xdd, 0x3d, 0x3d, 0xfd, 0xdf, 0x8d, 0x81, 0xf6, 0x45, 0xe4, 0xd3, 0xc5, + 0xce, 0x32, 0x8e, 0x78, 0x44, 0xb4, 0xe5, 0xf4, 0x8d, 0x6d, 0x80, 0x7e, 0x16, 0x26, 0x8c, 0xfa, + 0xf6, 0xe7, 0xb0, 0xde, 0xa7, 0xe7, 0x6e, 0xb2, 0xe0, 0x3d, 0x8f, 0x1d, 0x47, 0x3e, 0x25, 0x04, + 0x1a, 0x6e, 0xc2, 0xe7, 0xdd, 0xda, 0x83, 0xda, 0x13, 0xd3, 0x11, 0x6b, 0x01, 0x0b, 0xa3, 0xb0, + 0x5b, 0x57, 0xb0, 0x30, 0x0a, 0xed, 0x1f, 0x00, 0xf4, 0x3c, 0x8f, 0xb2, 0xec, 0xd4, 0x6b, 0x37, + 0xe4, 0xe9, 0x29, 0x5c, 0x93, 0x4d, 0x68, 0xce, 0x82, 0x15, 0x4d, 0x8f, 0xc9, 0x8d, 0xfd, 0x1c, + 0xf4, 0x31, 0xe5, 0xe3, 0x64, 0x4a, 0x3e, 0x80, 0x56, 0xc2, 0x68, 0x3c, 0x09, 0x7c, 0x75, 0x4c, + 0xc7, 0xed, 0xa1, 0x8f, 0xcc, 0x50, 0xe4, 0xf4, 0x3a, 0x5c, 0xdb, 0x97, 0xd0, 0x1a, 0x53, 0xde, + 0xa7, 0xcc, 0x23, 0xdf, 0x87, 0xb6, 0x2f, 0x65, 0x9e, 0xb8, 0x1e, 0x13, 0x67, 0xdb, 0xbb, 0xff, + 0xb7, 0xb3, 0x9c, 0xbe, 0xd9, 0x29, 0xeb, 0xe2, 0x80, 0x9f, 0xed, 0xc9, 0x1d, 0xd0, 0x97, 0xc9, + 0x74, 0x11, 0x78, 0x82, 0x6d, 0xc7, 0x51, 0x3b, 0xd2, 0x85, 0xd6, 0x32, 0x0e, 0x56, 0x2e, 0xa7, + 0x5d, 0x4d, 0x20, 0xd2, 0xad, 0xfd, 0x0a, 0x5a, 0xfb, 0x94, 0x0f, 0x97, 0x9c, 0x91, 0x6d, 0xb8, + 0x1d, 0x9c, 0x4f, 0x2e, 0x22, 0x3f, 0x38, 0x0f, 0xa8, 0x3f, 0x61, 0x41, 0xe8, 0x51, 0x71, 0xb1, + 0xe6, 0x6c, 0x04, 0xe7, 0xc7, 0x0a, 0x3e, 0x46, 0x30, 0xaa, 0xbd, 0x08, 0x2e, 0x02, 0x2e, 0xee, + 0x69, 0x3a, 0x72, 0x63, 0xff, 0xae, 0x06, 0xf0, 0x22, 0x8e, 0x5e, 0x33, 0x2a, 0x18, 0x7e, 0x08, + 0x86, 0x60, 0x92, 0x2a, 0xdf, 0x74, 0x5a, 0x62, 0x7f, 0xe8, 0xe7, 0x28, 0xce, 0x04, 0x0b, 0x4d, + 0xa1, 0x4e, 0x19, 0xb9, 0x0b, 0xe6, 0x94, 0x9e, 0x47, 0xb1, 0x38, 0xa6, 0x89, 0x63, 0x86, 0x04, + 0x1c, 0xfa, 0x05, 0x24, 0x67, 0xdd, 0x86, 0x38, 0xa8, 0x90, 0xa7, 0x2c, 0x17, 0xaa, 0x59, 0x14, + 0xea, 0x37, 0x35, 0x30, 0xf6, 0x29, 0xff, 0x59, 0x42, 0xe3, 0xb7, 0xc2, 0x85, 0x73, 0x37, 0x77, + 0xe1, 0xdc, 0xe5, 0xe4, 0x01, 0x34, 0x7c, 0xca, 0xa4, 0xc9, 0xda, 0xbb, 0x1d, 0x61, 0x63, 0x65, + 0x13, 0x47, 0x60, 0xc8, 0x47, 0xa0, 0xb1, 0x64, 0x2a, 0x84, 0xa9, 0x12, 0x20, 0x82, 0x3c, 0x82, + 0x86, 0xef, 0x72, 0x57, 0x08, 0xd4, 0xde, 0xdd, 0x10, 0x04, 0xb9, 0x1d, 0x1c, 0x81, 0xb4, 0x5f, + 0x81, 0x31, 0x4e, 0xc5, 0x48, 0xaf, 0xac, 0x15, 0x38, 0x2a, 0xcf, 0xab, 0x2b, 0xef, 0xc9, 0x2b, + 0xa5, 0x4c, 0xed, 0x94, 0x60, 0x9c, 0x4c, 0xc5, 0x8d, 0xf6, 0x2f, 0xc1, 0xd8, 0x5b, 0x04, 0x34, + 0xe4, 0x07, 0x01, 0x59, 0x87, 0x7a, 0x16, 0x5d, 0xf5, 0xc0, 0x27, 0xf7, 0x00, 0x44, 0xc8, 0xb9, + 0x33, 0x1a, 0x72, 0x15, 0x5f, 0x26, 0x42, 0x7a, 0x08, 0x20, 0x16, 0x68, 0x2b, 0x1a, 0x2b, 0xcb, + 0xe2, 0x12, 0x8d, 0xea, 0xd3, 0x55, 0x20, 0x1d, 0xd5, 0x10, 0xf4, 0x86, 0x04, 0xc8, 0x38, 0x5d, + 0xb8, 0xe1, 0x4c, 0xd8, 0xd4, 0x74, 0xc4, 0xda, 0xfe, 0x53, 0x0d, 0x4c, 0x79, 0x7d, 0xcf, 0xf3, + 0xae, 0xdc, 0x5f, 0x08, 0xf9, 0x7a, 0x29, 0xe4, 0xef, 0x80, 0xce, 0xbc, 0x39, 0xbd, 0x90, 0x41, + 0x68, 0x3a, 0x6a, 0x27, 0xe0, 0xd4, 0x8b, 0x29, 0x17, 0x97, 0x77, 0x1c, 0xb5, 0x13, 0xfe, 0x8c, + 0x66, 0x41, 0x28, 0xee, 0x36, 0x1c, 0xb9, 0x41, 0x81, 0xb8, 0x3b, 0x63, 0x5d, 0xfd, 0x81, 0x86, + 0x02, 0xe1, 0x3a, 0xb3, 0x67, 0xeb, 0x3a, 0x7b, 0xda, 0xc7, 0xd0, 0x96, 0x12, 0x1f, 0x09, 0x26, + 0x55, 0x99, 0x73, 0xd1, 0xea, 0xd7, 0x88, 0xa6, 0x15, 0x45, 0xb3, 0xbf, 0xce, 0x2c, 0x80, 0x49, + 0x5e, 0xe5, 0xb6, 0x09, 0x4d, 0x1e, 0x2d, 0x55, 0x16, 0x9a, 0x8e, 0xdc, 0x90, 0x6d, 0x30, 0x19, + 0xe5, 0x93, 0x4b, 0x8c, 0x00, 0x15, 0x4b, 0xb7, 0x52, 0x49, 0x45, 0x58, 0x38, 0x06, 0x4b, 0x03, + 0x64, 0x1b, 0xcc, 0x59, 0x46, 0xdb, 0x28, 0xd0, 0xee, 0x67, 0xb4, 0x33, 0xb5, 0xb2, 0x0f, 0x33, + 0xd5, 0xa8, 0xbb, 0xa2, 0x37, 0x14, 0x66, 0x13, 0x9a, 0x49, 0x98, 0x06, 0xb5, 0xe1, 0xc8, 0x8d, + 0xfd, 0xd7, 0x4c, 0xad, 0xd1, 0x8d, 0xd5, 0xfa, 0x00, 0x5a, 0x61, 0x34, 0xa1, 0xde, 0x3c, 0x52, + 0xbc, 0xf4, 0x30, 0x1a, 0x78, 0xf3, 0x88, 0x7c, 0x07, 0x1a, 0x73, 0xea, 0x62, 0x44, 0x69, 0x4f, + 0xda, 0xbb, 0x5d, 0x21, 0x7e, 0xc6, 0x7c, 0xe7, 0x80, 0xba, 0xfe, 0x20, 0xe4, 0xf1, 0x5b, 0x47, + 0x50, 0x61, 0x89, 0xf2, 0xa2, 0x90, 0x63, 0xc8, 0x36, 0x65, 0x89, 0x52, 0xdb, 0xad, 0x1f, 0x82, + 0x99, 0x11, 0x63, 0xf4, 0x7e, 0x45, 0xdf, 0x2a, 0xa1, 0x70, 0x89, 0x52, 0xad, 0xdc, 0x45, 0x92, + 0x7a, 0x4e, 0x6e, 0x7e, 0x5c, 0xff, 0xbc, 0x66, 0x7f, 0x99, 0x2a, 0xb3, 0x4f, 0xf9, 0x0d, 0x95, + 0x79, 0x04, 0xcd, 0xab, 0xfe, 0xc9, 0x6c, 0x2e, 0x71, 0x39, 0xdf, 0xf1, 0x7f, 0xc7, 0x77, 0x5c, + 0xe1, 0xfb, 0xf7, 0xcc, 0xfa, 0x7d, 0xba, 0xb8, 0x21, 0xe3, 0x4f, 0x54, 0x41, 0x43, 0xbe, 0xeb, + 0xaa, 0x41, 0x64, 0x3c, 0x76, 0x7e, 0x3e, 0x77, 0xb9, 0xaa, 0x72, 0x77, 0x40, 0x97, 0x85, 0x52, + 0x84, 0x53, 0xd3, 0x51, 0x3b, 0x51, 0x89, 0xe9, 0xe5, 0x64, 0x11, 0x30, 0x34, 0xbc, 0x26, 0x8a, + 0x34, 0xbd, 0x3c, 0x0a, 0x18, 0x2f, 0x26, 0xb2, 0x5e, 0xed, 0x5d, 0x73, 0x37, 0xf6, 0x45, 0xba, + 0x19, 0x8e, 0x58, 0xdb, 0x8f, 0xa0, 0x81, 0xb7, 0x91, 0x16, 0x68, 0xc7, 0xe3, 0x7d, 0x6b, 0x8d, + 0x98, 0xd0, 0x3c, 0x1d, 0x8e, 0x0e, 0xf7, 0xac, 0x1a, 0xc2, 0xc6, 0x67, 0x2f, 0xac, 0xba, 0xfd, + 0x0b, 0x00, 0x29, 0xdc, 0x49, 0xc4, 0x69, 0xae, 0x51, 0xad, 0xa8, 0xd1, 0x43, 0xa5, 0x51, 0x5d, + 0x68, 0x24, 0x2d, 0x75, 0x18, 0x9e, 0x47, 0x78, 0x44, 0xe9, 0xf2, 0xff, 0x98, 0x95, 0x97, 0x79, + 0x7f, 0x68, 0x32, 0x7a, 0x79, 0xe8, 0xdb, 0xbf, 0xd5, 0x52, 0xfb, 0x1d, 0xb3, 0x19, 0xb9, 0x0f, + 0xf5, 0x79, 0xa0, 0x2a, 0xec, 0xad, 0x82, 0x5d, 0x0e, 0x82, 0x83, 0x35, 0xa7, 0x3e, 0x0f, 0x88, + 0x0d, 0x9a, 0xeb, 0xa5, 0x65, 0x7f, 0xbd, 0x40, 0xd1, 0xf3, 0xbc, 0x83, 0x35, 0x07, 0x91, 0xe4, + 0x49, 0x5a, 0x82, 0xa4, 0xdf, 0xac, 0x02, 0x95, 0x28, 0x24, 0x07, 0x6b, 0x69, 0x59, 0xb2, 0x65, + 0xc1, 0x6e, 0x5c, 0xe1, 0x36, 0x4e, 0xa6, 0xc8, 0x0d, 0xfb, 0x04, 0x72, 0xc3, 0x1c, 0x15, 0x11, + 0x5e, 0xe1, 0x86, 0x70, 0xc1, 0x4d, 0x24, 0xb1, 0x0d, 0xda, 0x32, 0x99, 0x0a, 0xb3, 0x97, 0xb9, + 0x8d, 0x24, 0xb7, 0x65, 0x32, 0x45, 0x9a, 0x19, 0xe5, 0xaa, 0xe6, 0x15, 0x69, 0xf6, 0x29, 0x47, + 0x9a, 0x19, 0xe5, 0x42, 0x2a, 0xca, 0xbb, 0xc6, 0x55, 0xa9, 0x24, 0x0d, 0x93, 0x34, 0x3e, 0x5d, + 0x74, 0xcd, 0x2b, 0x34, 0x7d, 0xba, 0x40, 0x1a, 0x9f, 0x2e, 0xc8, 0xb7, 0xa1, 0x11, 0x46, 0x9c, + 0x76, 0xa1, 0xd0, 0xe1, 0x72, 0x4f, 0x1e, 0xac, 0x39, 0x02, 0xfd, 0xc2, 0x84, 0xd6, 0x31, 0x65, + 0xcc, 0x9d, 0x51, 0xfb, 0x8f, 0x75, 0x30, 0x4f, 0xd1, 0xa1, 0x7d, 0xd9, 0xce, 0xc0, 0x8b, 0xa9, + 0xcb, 0xa9, 0x3f, 0x51, 0xdd, 0x57, 0x73, 0x4c, 0x05, 0xe9, 0x71, 0xd1, 0xb2, 0x96, 0x7e, 0x8a, + 0x96, 0x03, 0x81, 0xa9, 0x20, 0x3d, 0x4e, 0x9e, 0x82, 0x8e, 0x43, 0x8e, 0xc7, 0x94, 0x79, 0xdf, + 0x39, 0x07, 0x29, 0x12, 0xf2, 0x10, 0xdd, 0xca, 0x94, 0x89, 0xa5, 0xa4, 0xf9, 0x0c, 0x87, 0x5e, + 0x65, 0x85, 0xf8, 0xd1, 0x0b, 0xf1, 0x83, 0xf1, 0x1e, 0x53, 0xd7, 0x47, 0x78, 0x4b, 0xe6, 0x08, + 0x6e, 0x53, 0x84, 0xb7, 0x42, 0x84, 0x91, 0x22, 0xbc, 0x95, 0x1c, 0x63, 0xbc, 0x05, 0x75, 0x45, + 0x8a, 0x98, 0x72, 0xc2, 0x11, 0x7b, 0xd9, 0xec, 0xd4, 0x28, 0x06, 0xd7, 0x8d, 0x62, 0xed, 0xf2, + 0x28, 0xf6, 0x7b, 0x0d, 0x0c, 0x61, 0x31, 0x6c, 0x29, 0x65, 0x8b, 0xd4, 0xaa, 0x16, 0xb9, 0x07, + 0xe0, 0xd3, 0x05, 0x2d, 0x1b, 0x4c, 0x41, 0x7a, 0x22, 0xd9, 0xa3, 0x70, 0x11, 0x84, 0x34, 0x2d, + 0xc9, 0x72, 0x97, 0xda, 0xa6, 0xf1, 0x1e, 0xdb, 0x14, 0x8c, 0xd0, 0xbc, 0xce, 0x08, 0x7a, 0xc9, + 0x08, 0xb9, 0xa6, 0xad, 0xeb, 0x34, 0x35, 0x4a, 0x9a, 0x16, 0x0b, 0x8b, 0x59, 0x2a, 0x2c, 0x59, + 0x45, 0x80, 0x62, 0x45, 0xc8, 0xdd, 0xd5, 0x2e, 0xba, 0xab, 0x68, 0xfc, 0x4e, 0xd9, 0xf8, 0x8f, + 0x61, 0x7d, 0xe1, 0x32, 0x3e, 0x61, 0x94, 0x86, 0x13, 0x1e, 0x5c, 0xd0, 0xee, 0x2d, 0x61, 0xa2, + 0x0e, 0x42, 0xc7, 0x94, 0x86, 0xa7, 0xc1, 0x05, 0x25, 0xdf, 0x85, 0xcd, 0x9c, 0xaa, 0x30, 0x32, + 0xad, 0x8b, 0xcb, 0x6f, 0xa7, 0xb4, 0x67, 0xe9, 0xe8, 0x64, 0xff, 0xb9, 0x06, 0x30, 0xa6, 0xf1, + 0x8a, 0xc6, 0x7b, 0x3c, 0xbe, 0x5a, 0xa1, 0x09, 0x34, 0xbc, 0x74, 0xa4, 0x6f, 0x3a, 0x62, 0x2d, + 0xa6, 0x15, 0xfa, 0x86, 0xab, 0x89, 0x47, 0xac, 0xc9, 0x33, 0xd0, 0x97, 0x6e, 0xec, 0x5e, 0x30, + 0xd5, 0x1a, 0xef, 0xaa, 0x6e, 0x90, 0x32, 0xde, 0x19, 0x09, 0xac, 0xec, 0x8e, 0x8a, 0x74, 0xeb, + 0x47, 0xd0, 0x2e, 0x80, 0xbf, 0xa9, 0x0f, 0x76, 0x8a, 0x7d, 0xf0, 0x9f, 0x99, 0xd8, 0x7d, 0x97, + 0xbb, 0xd7, 0x94, 0xdd, 0x07, 0xd0, 0x39, 0x8f, 0xa3, 0x8b, 0x49, 0x79, 0x74, 0x03, 0x84, 0x9d, + 0x49, 0xe7, 0x94, 0x63, 0x4e, 0xab, 0xc6, 0x5c, 0xee, 0xa5, 0x46, 0xd1, 0x4b, 0x9f, 0xaa, 0x29, + 0xa0, 0x29, 0x54, 0xfd, 0xb0, 0xa0, 0x2a, 0x0a, 0xf3, 0xbe, 0x31, 0x40, 0xff, 0x1f, 0x8d, 0x01, + 0x5f, 0x6b, 0xa9, 0xfa, 0xa3, 0x98, 0xb2, 0x6b, 0xd4, 0xb7, 0x40, 0x63, 0x71, 0xda, 0x5b, 0x71, + 0x49, 0x9e, 0x94, 0x3a, 0xeb, 0x66, 0x41, 0x70, 0x64, 0x53, 0x6c, 0xad, 0xe5, 0x81, 0xbb, 0x51, + 0x1d, 0xb8, 0x73, 0xc3, 0x34, 0x2b, 0xe1, 0x9b, 0x35, 0x5e, 0xbd, 0xdc, 0x78, 0x1f, 0xc3, 0x3a, + 0x77, 0x63, 0x1c, 0x00, 0x53, 0x6f, 0xb4, 0x04, 0xd3, 0x8e, 0x84, 0x2a, 0x7f, 0xd8, 0x70, 0xcb, + 0xf5, 0x78, 0x14, 0x67, 0x44, 0x86, 0x20, 0x6a, 0x0b, 0xa0, 0xa2, 0x51, 0x09, 0x6f, 0x5e, 0x9f, + 0xf0, 0xf6, 0x57, 0xaa, 0x71, 0xeb, 0x50, 0x1f, 0x9e, 0x58, 0x6b, 0xd8, 0xac, 0x87, 0x2f, 0x5f, + 0x5a, 0x35, 0x04, 0x9c, 0xf5, 0x2c, 0x0d, 0x01, 0x67, 0xa3, 0xbe, 0xd5, 0x20, 0x06, 0x34, 0xf6, + 0x87, 0x27, 0x03, 0xab, 0x89, 0xa0, 0xde, 0xde, 0xd8, 0xd2, 0x11, 0x74, 0x3a, 0x70, 0x8e, 0xad, + 0x56, 0xda, 0xf7, 0x0d, 0x04, 0x39, 0x83, 0x5e, 0xdf, 0x32, 0xe5, 0x6a, 0xef, 0x4b, 0x0b, 0x10, + 0xd9, 0x1f, 0x1c, 0x59, 0x6d, 0xfb, 0x75, 0xea, 0x8a, 0x63, 0xca, 0xdd, 0x1b, 0x8e, 0x38, 0xb6, + 0x1a, 0xee, 0xb5, 0x42, 0x83, 0xca, 0x3a, 0x8b, 0xfa, 0x5d, 0xba, 0x9f, 0x76, 0x5f, 0x2d, 0xeb, + 0xf6, 0x69, 0x29, 0x95, 0x3f, 0x4c, 0xbf, 0x4a, 0x2f, 0xc6, 0x51, 0xe2, 0x3f, 0x4e, 0x81, 0x87, + 0xa5, 0x98, 0xf8, 0x86, 0xd9, 0xa4, 0x98, 0x06, 0xf6, 0xdf, 0x6a, 0x60, 0x2a, 0xcd, 0xd9, 0x0c, + 0xdb, 0xa9, 0xc7, 0xe3, 0x85, 0x9a, 0x4e, 0x36, 0x2a, 0xf9, 0x8f, 0xed, 0x14, 0xd1, 0x48, 0x26, + 0xfe, 0x2b, 0xeb, 0x57, 0xc8, 0x30, 0x77, 0x90, 0x0c, 0xd1, 0x48, 0xb6, 0x8c, 0x29, 0x53, 0x06, + 0xda, 0xa8, 0x44, 0x2a, 0x92, 0x21, 0x1a, 0xc9, 0x2e, 0x68, 0xe5, 0x2f, 0x35, 0x77, 0x06, 0x92, + 0x21, 0x1a, 0xc9, 0x82, 0xf0, 0x3c, 0x2a, 0x35, 0xd0, 0xdc, 0x74, 0x48, 0x86, 0xe8, 0x62, 0xab, + 0xff, 0x4b, 0x56, 0x5f, 0x1c, 0xca, 0x96, 0xe4, 0x33, 0xd0, 0x19, 0x77, 0x79, 0x22, 0x5f, 0x2d, + 0xd6, 0xd5, 0xe4, 0x9f, 0x13, 0xec, 0xe0, 0x67, 0x4f, 0xb4, 0x6c, 0x49, 0x47, 0x3e, 0x06, 0x9d, + 0xc5, 0xab, 0x0b, 0x36, 0x2b, 0x0d, 0x63, 0x99, 0xb9, 0x1c, 0x85, 0x25, 0x8f, 0xa1, 0xe9, 0x2d, + 0x90, 0x4c, 0xbb, 0x32, 0xab, 0x20, 0x99, 0x44, 0xda, 0x5f, 0x80, 0x91, 0xde, 0x40, 0x3a, 0x60, + 0xec, 0x0d, 0x4f, 0x4e, 0x0f, 0x4f, 0xce, 0x06, 0xd6, 0x1a, 0x06, 0x64, 0xdf, 0x19, 0x8e, 0xac, + 0x1a, 0x69, 0x43, 0xcb, 0x19, 0x8c, 0x47, 0xc3, 0x93, 0xbe, 0x55, 0x97, 0x9b, 0xd1, 0x51, 0x6f, + 0x6f, 0x60, 0x69, 0xf6, 0x1f, 0xea, 0xd0, 0x1a, 0x53, 0xc6, 0x82, 0x28, 0xc4, 0xc4, 0x66, 0x72, + 0x99, 0xbf, 0xdf, 0x98, 0x0a, 0x72, 0xf8, 0x9e, 0x1f, 0xdd, 0xe7, 0x00, 0x6e, 0xc2, 0xe7, 0x93, + 0x05, 0x5d, 0xd1, 0x85, 0x0a, 0x96, 0x3b, 0x4a, 0x27, 0x71, 0x78, 0xa7, 0x97, 0xf0, 0xf9, 0x11, + 0x62, 0x1d, 0xd3, 0x4d, 0x97, 0xe4, 0x3e, 0xb4, 0x63, 0x7a, 0x11, 0x71, 0x3a, 0x71, 0x7d, 0x3f, + 0x56, 0x85, 0x04, 0x24, 0xa8, 0xe7, 0xfb, 0x71, 0xa5, 0xd0, 0x34, 0xab, 0x85, 0xa6, 0xf4, 0x1f, + 0xaf, 0x57, 0xfe, 0xe3, 0xb7, 0xc0, 0xc0, 0x7f, 0xf7, 0xc4, 0x9d, 0x51, 0x55, 0x4d, 0xb2, 0xbd, + 0xfd, 0x0c, 0xcc, 0x4c, 0x20, 0x34, 0xd2, 0x09, 0x66, 0xb9, 0x30, 0x57, 0xef, 0x64, 0x78, 0x62, + 0x81, 0x58, 0x9d, 0x9d, 0x1e, 0x58, 0x9b, 0x22, 0xa7, 0x87, 0xc3, 0x53, 0xeb, 0x23, 0x7b, 0x98, + 0x0e, 0xdb, 0x0e, 0xbd, 0x24, 0x0f, 0x40, 0x43, 0xbf, 0xd4, 0xde, 0xe9, 0x17, 0x44, 0xe1, 0x2f, + 0x3a, 0x5a, 0xae, 0xf4, 0xca, 0xa2, 0xac, 0xe1, 0x08, 0x8c, 0x3d, 0x03, 0x10, 0x39, 0x3b, 0x58, + 0xa1, 0x32, 0x0f, 0x41, 0x77, 0x3d, 0x1e, 0x44, 0xa1, 0x8a, 0x22, 0x53, 0x32, 0x8d, 0x13, 0xdf, + 0x51, 0x08, 0xec, 0xad, 0xa1, 0x9b, 0xfd, 0xb2, 0x8b, 0xf5, 0x4d, 0x8a, 0x85, 0xfd, 0x6b, 0x0d, + 0x3a, 0x3d, 0xcf, 0x8b, 0x92, 0x90, 0xdf, 0xf8, 0xae, 0x6b, 0x7d, 0x5d, 0x79, 0xa8, 0xd3, 0xfe, + 0xdd, 0x87, 0xba, 0xc6, 0x75, 0x33, 0x53, 0xb3, 0x3c, 0x33, 0xdd, 0x05, 0x33, 0x1b, 0x56, 0x84, + 0x73, 0x35, 0x74, 0xa0, 0x9c, 0x50, 0x2a, 0x81, 0xd1, 0xaa, 0x06, 0xc6, 0x4f, 0xa1, 0x23, 0xe2, + 0x51, 0x3e, 0x6a, 0xb0, 0xae, 0x21, 0xca, 0xe4, 0xb7, 0xd2, 0x76, 0x90, 0x19, 0x42, 0x84, 0xe5, + 0x58, 0x10, 0x39, 0x6d, 0x37, 0x5b, 0xb3, 0xec, 0xcd, 0xc5, 0xcc, 0xdf, 0x5c, 0xb6, 0xbe, 0x00, + 0xc8, 0xc9, 0x0b, 0x0f, 0x28, 0xb5, 0x6b, 0x1e, 0x50, 0xea, 0xa5, 0x07, 0x94, 0x10, 0x6e, 0x8f, + 0x93, 0x29, 0xf3, 0xe2, 0x60, 0x89, 0xf6, 0xbd, 0xb1, 0x1f, 0xb0, 0xbc, 0x26, 0xd3, 0xdc, 0x0d, + 0x4d, 0x96, 0x4c, 0x0f, 0xfd, 0xb4, 0xfe, 0x17, 0xff, 0xae, 0xcb, 0xf5, 0xff, 0x7b, 0xd0, 0x51, + 0xe5, 0xea, 0xa6, 0x57, 0x6d, 0x7f, 0x0c, 0x46, 0x5a, 0xdb, 0xb3, 0xa6, 0xb6, 0x96, 0x35, 0x35, + 0xd1, 0x1f, 0x5f, 0x8d, 0xac, 0xfa, 0xf6, 0x36, 0x34, 0xf0, 0x1c, 0x01, 0xd0, 0xf7, 0x9c, 0x41, + 0xef, 0x14, 0x53, 0x07, 0x40, 0x3f, 0x1b, 0xf5, 0x71, 0x5d, 0xc3, 0x75, 0x7f, 0x70, 0x34, 0x38, + 0x1d, 0x58, 0xf5, 0xdd, 0x9f, 0x40, 0xe3, 0x04, 0xeb, 0xd2, 0x33, 0x68, 0x2b, 0x71, 0x8e, 0xa2, + 0x68, 0x49, 0x2a, 0x19, 0xb3, 0x55, 0x29, 0x80, 0xf6, 0xda, 0x93, 0xda, 0x67, 0xb5, 0xdd, 0x7f, + 0xd4, 0x40, 0x1f, 0x2d, 0x12, 0xfc, 0xdb, 0xfc, 0x14, 0x8c, 0x97, 0x41, 0x4c, 0x0f, 0x22, 0x46, + 0x4b, 0x87, 0x1d, 0x7a, 0xb9, 0xb5, 0x51, 0xa9, 0xb7, 0xf6, 0x1a, 0x79, 0x0a, 0x2d, 0xe5, 0x67, + 0x72, 0xfb, 0x8a, 0xd7, 0xb7, 0xe4, 0xf3, 0xa2, 0x7a, 0x2c, 0x5f, 0x23, 0x9f, 0x40, 0x53, 0xd8, + 0x8e, 0x6c, 0xe4, 0x76, 0x7c, 0x27, 0xe1, 0x73, 0xe8, 0x14, 0x7d, 0x48, 0x54, 0x89, 0xab, 0xba, + 0xb5, 0x7a, 0xec, 0x69, 0xd6, 0x39, 0x94, 0x30, 0x45, 0xc7, 0x54, 0x88, 0xa7, 0xba, 0x78, 0xd1, + 0x7f, 0xf6, 0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x6c, 0xd8, 0x0a, 0x1b, 0xe0, 0x17, 0x00, 0x00, } diff --git a/server/plugins.go b/server/plugins.go index 33e8f45bf..64823b782 100644 --- a/server/plugins.go +++ b/server/plugins.go @@ -3,6 +3,7 @@ package main import ( "encoding/json" + "errors" "log" "strings" "time" @@ -23,44 +24,163 @@ const ( plgSet plgDel plgNote - - plgData = 1 << iota + plgData plgMeta plgPres plgInfo plgClientMask = plgHi | plgAcc | plgLogin | plgSub | plgLeave | plgPub | plgGet | plgSet | plgDel | plgNote plgServerMask = plgData | plgMeta | plgPres | plgInfo -) -const ( plgActCreate = 1 << iota plgActUpd plgActDel + + plgActMask = plgActCreate | plgActUpd | plgActDel + + plgTopicMe = 1 << iota + plgTopicFnd + plgTopicP2P + plgTopicGrp + plgTopicNew + + plgTopicCatMask = plgTopicMe | plgTopicFnd | plgTopicP2P | plgTopicGrp + + plgFilterByTopicType = 1 << iota + plgFilterByPacket + plgFilterByAction ) var ( - plgClientNames = []string{"hi", "acc", "login", "sub", "leave", "pub", "get", "set", "del", "note"} - plgServerNames = []string{"data", "meta", "pres", "info"} + plgPacketNames = []string{ + "hi", "acc", "login", "sub", "leave", "pub", "get", "set", "del", "note", + "data", "meta", "pres", "info", + } + + plgTopicCatNames = []string{"me", "fnd", "p2p", "grp", "new"} ) type PluginFilter struct { - packet int - topic []string - action int + byPacket int + byTopicType int + byAction int } -// Filters for individual RPC call. Filter strings are formatted as follows: -// : : [CUD] +func ParsePluginFilter(s *string, filterBy int) (*PluginFilter, error) { + if s == nil { + return nil, nil + } + + parseByName := func(parts []string, options []string, def int) (int, error) { + var result int + + // Iterate over filter parts + for _, inp := range parts { + if inp != "" { + inp = strings.ToLower(inp) + // Split string like "hi,login,pres" or "me,p2p,fnd" + values := strings.Split(inp, ",") + // For each value in the input string, try to find it in the options set + for _, val := range values { + i := 0 + // Iterate over the options, i.e find "hi" in the slice of packet names + for i = range options { + if options[i] == val { + result |= 1 << uint(i) + break + } + } + + if result != 0 && i == len(options) { + // Mix of known and unknown options in the input + return 0, errors.New("plugin: unknown value in filter " + val) + } + } + + if result != 0 { + // Found and parsed the right part + break + } + } + } + + // If the filter value is not defined, use default. + if result == 0 { + result = def + } + + return result, nil + } + + parseAction := func(parts []string) int { + var result int + for _, inp := range parts { + + for _, char := range inp { + switch char { + case 'c', 'C': + result |= plgActCreate + case 'u', 'U': + result |= plgActUpd + case 'd', 'D': + result |= plgActDel + default: + // Unknown symbol means this is not an action string. + result = 0 + break + } + } + + if result != 0 { + // Found and parsed actions. + break + } + } + if result == 0 { + result = plgActMask + } + return result + } + + filter := PluginFilter{} + parts := strings.Split(*s, ";") + var err error + + if filterBy&plgFilterByPacket != 0 { + if filter.byPacket, err = parseByName(parts, plgPacketNames, plgClientMask|plgServerMask); err != nil { + return nil, err + } + } + + if filterBy&plgFilterByTopicType != 0 { + if filter.byTopicType, err = parseByName(parts, plgTopicCatNames, plgTopicCatMask); err != nil { + return nil, err + } + } + + if filterBy&plgFilterByAction != 0 { + filter.byAction = parseAction(parts) + } + + return &filter, nil +} + +// Filters for an individual RPC call. Filter strings are formatted as follows: +// : : // For instance: -// "acc,login::CU" - grab packets {acc} or {login}, any topic (no filtering), Create or Update action -// "pub +// "acc,login::CU" - grab packets {acc} or {login}; no filtering by topic, Create or Update action +// "pub,pres:me,p2p:" type PluginRPCFilterConfig struct { - FireHose *string // Filter by topic type, exact name, packet type. - Account *string // Filter by CUD, exact user name, maybe AuthLevel? - Topic *string // Filter by CUD, topic type, exact name - Subscription *string // Filter by CUD, topic type, exact topic name, exact user name - Message *string // Filter by C.D, topic type, exact topic name, exact user name + // Filter by packet name, topic type [or exact name - not supported yet]. 2D: "pub,pres;p2p,me" + FireHose *string `json:"fire_hose"` + // Filter by CUD, [exact user name - not supported yet]. 1D: "C" + Account *string `json:"account"` + // Filter by CUD, topic type[, exact name]: "p2p;CU" + Topic *string `json:"topic"` + // Filter by CUD, topic type[, exact topic name, exact user name]: "CU" + Subscription *string `json:"subscription"` + // Filter by C.D, topic type[, exact topic name, exact user name]: "grp;CD" + Message *string `json:"message"` } type PluginConfig struct { @@ -70,7 +190,7 @@ type PluginConfig struct { // Microseconds to wait before timeout Timeout int64 `json:"timeout"` // Filters for RPC calls: when to call vs when to skip the call - Filters PluginEventFilterConfig `json:"filters"` + Filters PluginRPCFilterConfig `json:"filters"` // What should the server do if plugin failed: HTTP error code FailureCode int `json:"failure_code"` // HTTP Error message to go with the code @@ -80,15 +200,18 @@ type PluginConfig struct { } type Plugin struct { - name string - timeout time.Duration - isFilter bool - messages uint32 - topics []string - failureCode int - failureText string - network string - addr string + name string + timeout time.Duration + // Filters for individual methods + filterFireHose *PluginFilter + filterAccount *PluginFilter + filterTopic *PluginFilter + filterSubscription *PluginFilter + filterMessage *PluginFilter + failureCode int + failureText string + network string + addr string conn *grpc.ClientConn client pbx.PluginClient @@ -119,41 +242,32 @@ func pluginsInit(configString json.RawMessage) { log.Fatalf("plugins: duplicate name '%s'", conf.Name) } - var names []string - var mask uint32 - if conf.Type == "filter" { - names = plgFilterNames - mask = plgFilterMask - } else { - names = plgHandlerNames - mask = plgHandlerMask - } - - var msgFilter uint32 - if conf.MessageFilter == nil || (len(conf.MessageFilter) == 1 && conf.MessageFilter[0] == "*") { - msgFilter = mask - } else { - for _, msg := range conf.MessageFilter { - idx := findInSlice(msg, names) - if idx < 0 { - log.Fatalf("plugins: unknown message name '%s'", msg) - } - msgFilter |= (1 << uint(idx)) - } - } - - if msgFilter == 0 { - continue - } - plugins[count] = Plugin{ name: conf.Name, timeout: time.Duration(conf.Timeout) * time.Microsecond, - isFilter: conf.Type == "filter", failureCode: conf.FailureCode, failureText: conf.FailureMessage, - messages: msgFilter, - topics: conf.TopicFilter, + } + var err error + if plugins[count].filterFireHose, err = + ParsePluginFilter(conf.Filters.FireHose, plgFilterByTopicType|plgFilterByPacket); err != nil { + log.Fatal("plugins: bad FireHose filter", err) + } + if plugins[count].filterAccount, err = + ParsePluginFilter(conf.Filters.Account, plgFilterByAction); err != nil { + log.Fatal("plugins: bad Account filter", err) + } + if plugins[count].filterTopic, err = + ParsePluginFilter(conf.Filters.Topic, plgFilterByTopicType|plgFilterByAction); err != nil { + log.Fatal("plugins: bad FireHose filter", err) + } + if plugins[count].filterSubscription, err = + ParsePluginFilter(conf.Filters.Subscription, plgFilterByTopicType|plgFilterByAction); err != nil { + log.Fatal("plugins: bad Subscription filter", err) + } + if plugins[count].filterMessage, err = + ParsePluginFilter(conf.Filters.Message, plgFilterByTopicType|plgFilterByAction); err != nil { + log.Fatal("plugins: bad Message filter", err) } if parts := strings.SplitN(conf.ServiceAddr, "://", 2); len(parts) < 2 { @@ -163,7 +277,6 @@ func pluginsInit(configString json.RawMessage) { plugins[count].addr = parts[1] } - var err error plugins[count].conn, err = grpc.Dial(plugins[count].addr, grpc.WithInsecure()) if err != nil { log.Fatalf("plugins: connection failure %v", err) @@ -212,9 +325,9 @@ func pluginGenerateClientReq(sess *Session, msg *ClientComMessage) *pbx.ClientRe Language: sess.lang}} } -func pluginHandler(sess *Session, msg *ClientComMessage) *ServerComMessage { +func pluginFireHose(sess *Session, msg *ClientComMessage) (*ClientComMessage, *ServerComMessage) { if plugins == nil { - return nil + return nil, nil } var req *pbx.ClientReq @@ -223,7 +336,7 @@ func pluginHandler(sess *Session, msg *ClientComMessage) *ServerComMessage { var topic string ts := time.Now().UTC().Round(time.Millisecond) for _, p := range plugins { - if p.isFilter { + if p.filterFireHose == nil { continue } @@ -240,14 +353,14 @@ func pluginHandler(sess *Session, msg *ClientComMessage) *ServerComMessage { } else { ctx = context.Background() } - if resp, err := p.client.ClientMessage(ctx, req); err == nil { - // Response code 0 means default processing - if resp.GetCode() == 0 { + if resp, err := p.client.FireHose(ctx, req); err == nil { + // Response code CONTINUE means default processing + if resp.GetStatus() == pbx.ServerResp_CONTINUE { continue } - // This plugin returned non-zero. Subsequent plugins wil not be called. - return &ServerComMessage{Ctrl: &MsgServerCtrl{ + // This plugin returned non-zero. + return nil, &ServerComMessage{Ctrl: &MsgServerCtrl{ Id: id, Code: int(resp.GetCode()), Text: resp.GetText(), @@ -256,7 +369,7 @@ func pluginHandler(sess *Session, msg *ClientComMessage) *ServerComMessage { } else if p.failureCode != 0 { // Plugin failed and it's configured to stop futher processing. log.Println("plugin: failed,", p.name, err) - return &ServerComMessage{Ctrl: &MsgServerCtrl{ + return nil, &ServerComMessage{Ctrl: &MsgServerCtrl{ Id: id, Code: p.failureCode, Text: p.failureText, @@ -268,18 +381,21 @@ func pluginHandler(sess *Session, msg *ClientComMessage) *ServerComMessage { } } + return nil, nil +} + +func pluginAccount(msg *ServerComMessage) *ServerComMessage { return nil } -func pluginFilter(msg *ServerComMessage) *ServerComMessage { +func pluginTopic(msg *ServerComMessage) *ServerComMessage { return nil } -func findInSlice(needle string, haystack []string) int { - for i, val := range haystack { - if val == needle { - return i - } - } - return -1 +func pluginSubscription(msg *ServerComMessage) *ServerComMessage { + return nil +} + +func pluginMessage(msg *ServerComMessage) *ServerComMessage { + return nil } diff --git a/server/session.go b/server/session.go index ca6b57fe3..f6a690efb 100644 --- a/server/session.go +++ b/server/session.go @@ -192,9 +192,13 @@ func (s *Session) dispatch(msg *ClientComMessage) { defer s.rw.Unlock() } - if resp := pluginHandler(s, msg); resp != nil { + if msg, resp := pluginFireHose(s, msg); resp != nil { + // Plugin provided a response. No further processing is needed. s.queueOut(resp) return + } else if msg == nil { + // Plugin requested to silently drop the request. + return } switch {