diff --git a/api/client/webclient/webclient.go b/api/client/webclient/webclient.go index 7a30ea67ea5d9..cdf94bc88b6a0 100644 --- a/api/client/webclient/webclient.go +++ b/api/client/webclient/webclient.go @@ -296,6 +296,8 @@ type PingResponse struct { ServerVersion string `json:"server_version"` // MinClientVersion is the minimum client version required by the server. MinClientVersion string `json:"min_client_version"` + // AutoUpdateSettings contains the auto update settings. + AutoUpdate AutoUpdateSettings `json:"auto_update"` // ClusterName contains the name of the Teleport cluster. ClusterName string `json:"cluster_name"` @@ -329,6 +331,14 @@ type ProxySettings struct { AssistEnabled bool `json:"assist_enabled"` } +// AutoUpdateSettings contains information about the auto update requirements. +type AutoUpdateSettings struct { + // ToolsVersion defines the version of {tsh, tctl} for client auto update. + ToolsVersion string `json:"tools_version"` + // ToolsAutoUpdate indicates if the requesting tools client should be updated. + ToolsAutoUpdate bool `json:"tools_auto_update"` +} + // KubeProxySettings is kubernetes proxy settings type KubeProxySettings struct { // Enabled is true when kubernetes proxy is enabled diff --git a/api/gen/proto/go/teleport/autoupdate/v1/autoupdate.pb.go b/api/gen/proto/go/teleport/autoupdate/v1/autoupdate.pb.go index eff70443d2f28..ffc36aeb70163 100644 --- a/api/gen/proto/go/teleport/autoupdate/v1/autoupdate.pb.go +++ b/api/gen/proto/go/teleport/autoupdate/v1/autoupdate.pb.go @@ -122,8 +122,7 @@ type AutoUpdateConfigSpec struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // ToolsAutoupdate encodes the feature flag to enable/disable tools autoupdates. - ToolsAutoupdate bool `protobuf:"varint,1,opt,name=tools_autoupdate,json=toolsAutoupdate,proto3" json:"tools_autoupdate,omitempty"` + Tools *AutoUpdateConfigSpecTools `protobuf:"bytes,2,opt,name=tools,proto3" json:"tools,omitempty"` } func (x *AutoUpdateConfigSpec) Reset() { @@ -158,11 +157,60 @@ func (*AutoUpdateConfigSpec) Descriptor() ([]byte, []int) { return file_teleport_autoupdate_v1_autoupdate_proto_rawDescGZIP(), []int{1} } -func (x *AutoUpdateConfigSpec) GetToolsAutoupdate() bool { +func (x *AutoUpdateConfigSpec) GetTools() *AutoUpdateConfigSpecTools { if x != nil { - return x.ToolsAutoupdate + return x.Tools } - return false + return nil +} + +// AutoUpdateConfigSpecTools encodes the parameters for client tools auto updates. +type AutoUpdateConfigSpecTools struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Mode defines state of the client tools auto update. + Mode string `protobuf:"bytes,1,opt,name=mode,proto3" json:"mode,omitempty"` +} + +func (x *AutoUpdateConfigSpecTools) Reset() { + *x = AutoUpdateConfigSpecTools{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AutoUpdateConfigSpecTools) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AutoUpdateConfigSpecTools) ProtoMessage() {} + +func (x *AutoUpdateConfigSpecTools) ProtoReflect() protoreflect.Message { + mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AutoUpdateConfigSpecTools.ProtoReflect.Descriptor instead. +func (*AutoUpdateConfigSpecTools) Descriptor() ([]byte, []int) { + return file_teleport_autoupdate_v1_autoupdate_proto_rawDescGZIP(), []int{2} +} + +func (x *AutoUpdateConfigSpecTools) GetMode() string { + if x != nil { + return x.Mode + } + return "" } // AutoUpdateVersion is a resource singleton with version required for @@ -182,7 +230,7 @@ type AutoUpdateVersion struct { func (x *AutoUpdateVersion) Reset() { *x = AutoUpdateVersion{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[2] + mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -195,7 +243,7 @@ func (x *AutoUpdateVersion) String() string { func (*AutoUpdateVersion) ProtoMessage() {} func (x *AutoUpdateVersion) ProtoReflect() protoreflect.Message { - mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[2] + mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -208,7 +256,7 @@ func (x *AutoUpdateVersion) ProtoReflect() protoreflect.Message { // Deprecated: Use AutoUpdateVersion.ProtoReflect.Descriptor instead. func (*AutoUpdateVersion) Descriptor() ([]byte, []int) { - return file_teleport_autoupdate_v1_autoupdate_proto_rawDescGZIP(), []int{2} + return file_teleport_autoupdate_v1_autoupdate_proto_rawDescGZIP(), []int{3} } func (x *AutoUpdateVersion) GetKind() string { @@ -252,14 +300,13 @@ type AutoUpdateVersionSpec struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // ToolsVersion is the semantic version required for tools autoupdates. - ToolsVersion string `protobuf:"bytes,1,opt,name=tools_version,json=toolsVersion,proto3" json:"tools_version,omitempty"` + Tools *AutoUpdateVersionSpecTools `protobuf:"bytes,2,opt,name=tools,proto3" json:"tools,omitempty"` } func (x *AutoUpdateVersionSpec) Reset() { *x = AutoUpdateVersionSpec{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[3] + mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -272,7 +319,7 @@ func (x *AutoUpdateVersionSpec) String() string { func (*AutoUpdateVersionSpec) ProtoMessage() {} func (x *AutoUpdateVersionSpec) ProtoReflect() protoreflect.Message { - mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[3] + mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -285,12 +332,62 @@ func (x *AutoUpdateVersionSpec) ProtoReflect() protoreflect.Message { // Deprecated: Use AutoUpdateVersionSpec.ProtoReflect.Descriptor instead. func (*AutoUpdateVersionSpec) Descriptor() ([]byte, []int) { - return file_teleport_autoupdate_v1_autoupdate_proto_rawDescGZIP(), []int{3} + return file_teleport_autoupdate_v1_autoupdate_proto_rawDescGZIP(), []int{4} +} + +func (x *AutoUpdateVersionSpec) GetTools() *AutoUpdateVersionSpecTools { + if x != nil { + return x.Tools + } + return nil } -func (x *AutoUpdateVersionSpec) GetToolsVersion() string { +// AutoUpdateVersionSpecTools encodes the parameters for client tools auto updates. +type AutoUpdateVersionSpecTools struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // TargetVersion specifies the semantic version required for tools to establish a connection with the cluster. + // Client tools after connection to the cluster going to be updated to this version automatically. + TargetVersion string `protobuf:"bytes,1,opt,name=target_version,json=targetVersion,proto3" json:"target_version,omitempty"` +} + +func (x *AutoUpdateVersionSpecTools) Reset() { + *x = AutoUpdateVersionSpecTools{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AutoUpdateVersionSpecTools) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AutoUpdateVersionSpecTools) ProtoMessage() {} + +func (x *AutoUpdateVersionSpecTools) ProtoReflect() protoreflect.Message { + mi := &file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AutoUpdateVersionSpecTools.ProtoReflect.Descriptor instead. +func (*AutoUpdateVersionSpecTools) Descriptor() ([]byte, []int) { + return file_teleport_autoupdate_v1_autoupdate_proto_rawDescGZIP(), []int{5} +} + +func (x *AutoUpdateVersionSpecTools) GetTargetVersion() string { if x != nil { - return x.ToolsVersion + return x.TargetVersion } return "" } @@ -317,35 +414,50 @@ var file_teleport_autoupdate_v1_autoupdate_proto_rawDesc = []byte{ 0x73, 0x70, 0x65, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x22, 0x41, + 0x6e, 0x66, 0x69, 0x67, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x22, 0x77, 0x0a, 0x14, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x53, 0x70, 0x65, 0x63, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x5f, - 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0f, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x41, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x22, 0xd9, 0x01, 0x0a, 0x11, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, - 0x75, 0x62, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, - 0x75, 0x62, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x38, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x68, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x41, 0x0a, 0x04, 0x73, 0x70, - 0x65, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x2e, 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, 0x73, 0x70, 0x65, 0x63, 0x22, 0x3c, 0x0a, - 0x15, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x5f, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x74, - 0x6f, 0x6f, 0x6c, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x56, 0x5a, 0x54, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, - 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x69, 0x67, 0x53, 0x70, 0x65, 0x63, 0x12, 0x47, 0x0a, 0x05, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, + 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, + 0x70, 0x65, 0x63, 0x54, 0x6f, 0x6f, 0x6c, 0x73, 0x52, 0x05, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x4a, + 0x04, 0x08, 0x01, 0x10, 0x02, 0x52, 0x10, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x5f, 0x61, 0x75, 0x74, + 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x22, 0x2f, 0x0a, 0x19, 0x41, 0x75, 0x74, 0x6f, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x70, 0x65, 0x63, 0x54, + 0x6f, 0x6f, 0x6c, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x22, 0xd9, 0x01, 0x0a, 0x11, 0x41, 0x75, 0x74, + 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, + 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, + 0x6e, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x75, 0x62, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x18, 0x0a, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x41, 0x0a, 0x04, 0x73, 0x70, 0x65, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x61, 0x75, 0x74, 0x6f, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x52, 0x04, + 0x73, 0x70, 0x65, 0x63, 0x22, 0x76, 0x0a, 0x15, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x12, 0x48, 0x0a, + 0x05, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x54, 0x6f, 0x6f, 0x6c, 0x73, + 0x52, 0x05, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x52, 0x0d, 0x74, + 0x6f, 0x6f, 0x6c, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x43, 0x0a, 0x1a, + 0x41, 0x75, 0x74, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x53, 0x70, 0x65, 0x63, 0x54, 0x6f, 0x6f, 0x6c, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x61, + 0x72, 0x67, 0x65, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x42, 0x56, 0x5a, 0x54, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x61, + 0x75, 0x74, 0x6f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -360,24 +472,28 @@ func file_teleport_autoupdate_v1_autoupdate_proto_rawDescGZIP() []byte { return file_teleport_autoupdate_v1_autoupdate_proto_rawDescData } -var file_teleport_autoupdate_v1_autoupdate_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_teleport_autoupdate_v1_autoupdate_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_teleport_autoupdate_v1_autoupdate_proto_goTypes = []interface{}{ - (*AutoUpdateConfig)(nil), // 0: teleport.autoupdate.v1.AutoUpdateConfig - (*AutoUpdateConfigSpec)(nil), // 1: teleport.autoupdate.v1.AutoUpdateConfigSpec - (*AutoUpdateVersion)(nil), // 2: teleport.autoupdate.v1.AutoUpdateVersion - (*AutoUpdateVersionSpec)(nil), // 3: teleport.autoupdate.v1.AutoUpdateVersionSpec - (*v1.Metadata)(nil), // 4: teleport.header.v1.Metadata + (*AutoUpdateConfig)(nil), // 0: teleport.autoupdate.v1.AutoUpdateConfig + (*AutoUpdateConfigSpec)(nil), // 1: teleport.autoupdate.v1.AutoUpdateConfigSpec + (*AutoUpdateConfigSpecTools)(nil), // 2: teleport.autoupdate.v1.AutoUpdateConfigSpecTools + (*AutoUpdateVersion)(nil), // 3: teleport.autoupdate.v1.AutoUpdateVersion + (*AutoUpdateVersionSpec)(nil), // 4: teleport.autoupdate.v1.AutoUpdateVersionSpec + (*AutoUpdateVersionSpecTools)(nil), // 5: teleport.autoupdate.v1.AutoUpdateVersionSpecTools + (*v1.Metadata)(nil), // 6: teleport.header.v1.Metadata } var file_teleport_autoupdate_v1_autoupdate_proto_depIdxs = []int32{ - 4, // 0: teleport.autoupdate.v1.AutoUpdateConfig.metadata:type_name -> teleport.header.v1.Metadata + 6, // 0: teleport.autoupdate.v1.AutoUpdateConfig.metadata:type_name -> teleport.header.v1.Metadata 1, // 1: teleport.autoupdate.v1.AutoUpdateConfig.spec:type_name -> teleport.autoupdate.v1.AutoUpdateConfigSpec - 4, // 2: teleport.autoupdate.v1.AutoUpdateVersion.metadata:type_name -> teleport.header.v1.Metadata - 3, // 3: teleport.autoupdate.v1.AutoUpdateVersion.spec:type_name -> teleport.autoupdate.v1.AutoUpdateVersionSpec - 4, // [4:4] is the sub-list for method output_type - 4, // [4:4] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 2, // 2: teleport.autoupdate.v1.AutoUpdateConfigSpec.tools:type_name -> teleport.autoupdate.v1.AutoUpdateConfigSpecTools + 6, // 3: teleport.autoupdate.v1.AutoUpdateVersion.metadata:type_name -> teleport.header.v1.Metadata + 4, // 4: teleport.autoupdate.v1.AutoUpdateVersion.spec:type_name -> teleport.autoupdate.v1.AutoUpdateVersionSpec + 5, // 5: teleport.autoupdate.v1.AutoUpdateVersionSpec.tools:type_name -> teleport.autoupdate.v1.AutoUpdateVersionSpecTools + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_teleport_autoupdate_v1_autoupdate_proto_init() } @@ -411,7 +527,7 @@ func file_teleport_autoupdate_v1_autoupdate_proto_init() { } } file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AutoUpdateVersion); i { + switch v := v.(*AutoUpdateConfigSpecTools); i { case 0: return &v.state case 1: @@ -423,6 +539,18 @@ func file_teleport_autoupdate_v1_autoupdate_proto_init() { } } file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AutoUpdateVersion); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AutoUpdateVersionSpec); i { case 0: return &v.state @@ -434,6 +562,18 @@ func file_teleport_autoupdate_v1_autoupdate_proto_init() { return nil } } + file_teleport_autoupdate_v1_autoupdate_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AutoUpdateVersionSpecTools); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -441,7 +581,7 @@ func file_teleport_autoupdate_v1_autoupdate_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_teleport_autoupdate_v1_autoupdate_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 6, NumExtensions: 0, NumServices: 0, }, diff --git a/api/proto/teleport/autoupdate/v1/autoupdate.proto b/api/proto/teleport/autoupdate/v1/autoupdate.proto index 1987407fe7d81..b4e557549b316 100644 --- a/api/proto/teleport/autoupdate/v1/autoupdate.proto +++ b/api/proto/teleport/autoupdate/v1/autoupdate.proto @@ -33,8 +33,15 @@ message AutoUpdateConfig { // AutoUpdateConfigSpec encodes the parameters of the autoupdate config object. message AutoUpdateConfigSpec { - // ToolsAutoupdate encodes the feature flag to enable/disable tools autoupdates. - bool tools_autoupdate = 1; + reserved 1; + reserved "tools_autoupdate"; // ToolsAutoupdate is replaced by tools.mode. + AutoUpdateConfigSpecTools tools = 2; +} + +// AutoUpdateConfigSpecTools encodes the parameters for client tools auto updates. +message AutoUpdateConfigSpecTools { + // Mode defines state of the client tools auto update. + string mode = 1; } // AutoUpdateVersion is a resource singleton with version required for @@ -50,6 +57,14 @@ message AutoUpdateVersion { // AutoUpdateVersionSpec encodes the parameters of the autoupdate versions. message AutoUpdateVersionSpec { - // ToolsVersion is the semantic version required for tools autoupdates. - string tools_version = 1; + reserved 1; + reserved "tools_version"; // ToolsVersion is replaced by tools.target_version. + AutoUpdateVersionSpecTools tools = 2; +} + +// AutoUpdateVersionSpecTools encodes the parameters for client tools auto updates. +message AutoUpdateVersionSpecTools { + // TargetVersion specifies the semantic version required for tools to establish a connection with the cluster. + // Client tools after connection to the cluster going to be updated to this version automatically. + string target_version = 1; } diff --git a/api/proto/teleport/legacy/types/events/events.proto b/api/proto/teleport/legacy/types/events/events.proto index 8dc8fbeab3a72..43cdde01e42f1 100644 --- a/api/proto/teleport/legacy/types/events/events.proto +++ b/api/proto/teleport/legacy/types/events/events.proto @@ -5865,6 +5865,13 @@ message AutoUpdateConfigCreate { (gogoproto.embed) = true, (gogoproto.jsontag) = "" ]; + + // Status indicates whether the creation was successful. + Status Status = 5 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; } // AutoUpdateConfigUpdate is emitted when an auto update config is updated. @@ -5896,6 +5903,13 @@ message AutoUpdateConfigUpdate { (gogoproto.embed) = true, (gogoproto.jsontag) = "" ]; + + // ResourceMetadata is a common resource event metadata + ResourceMetadata Resource = 5 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; } // AutoUpdateConfigDelete is emitted when an auto update config is deleted. @@ -5927,6 +5941,13 @@ message AutoUpdateConfigDelete { (gogoproto.embed) = true, (gogoproto.jsontag) = "" ]; + + // Status indicates whether the deletion was successful. + Status Status = 5 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; } // AutoUpdateVersionCreate is emitted when an auto update version is created. @@ -5958,6 +5979,13 @@ message AutoUpdateVersionCreate { (gogoproto.embed) = true, (gogoproto.jsontag) = "" ]; + + // Status indicates whether the creation was successful. + Status Status = 5 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; } // AutoUpdateVersionUpdate is emitted when an auto update version is updated. @@ -5989,6 +6017,13 @@ message AutoUpdateVersionUpdate { (gogoproto.embed) = true, (gogoproto.jsontag) = "" ]; + + // ResourceMetadata is a common resource event metadata + ResourceMetadata Resource = 5 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; } // AutoUpdateVersionDelete is emitted when an auto update version is deleted. @@ -6020,4 +6055,11 @@ message AutoUpdateVersionDelete { (gogoproto.embed) = true, (gogoproto.jsontag) = "" ]; + + // Status indicates whether the deletion was successful. + Status Status = 5 [ + (gogoproto.nullable) = false, + (gogoproto.embed) = true, + (gogoproto.jsontag) = "" + ]; } diff --git a/api/types/autoupdate/config.go b/api/types/autoupdate/config.go index 5be3db89fd0c5..d61c35eccf0c2 100644 --- a/api/types/autoupdate/config.go +++ b/api/types/autoupdate/config.go @@ -26,6 +26,13 @@ import ( "github.com/gravitational/teleport/api/types" ) +const ( + // ToolsUpdateModeEnabled enables client tools automatic updates. + ToolsUpdateModeEnabled = "enabled" + // ToolsUpdateModeDisabled disables client tools automatic updates. + ToolsUpdateModeDisabled = "disabled" +) + // NewAutoUpdateConfig creates a new auto update configuration resource. func NewAutoUpdateConfig(spec *autoupdate.AutoUpdateConfigSpec) (*autoupdate.AutoUpdateConfig, error) { config := &autoupdate.AutoUpdateConfig{ @@ -58,6 +65,11 @@ func ValidateAutoUpdateConfig(c *autoupdate.AutoUpdateConfig) error { if c.Spec == nil { return trace.BadParameter("Spec is nil") } + if c.Spec.Tools != nil { + if c.Spec.Tools.Mode != ToolsUpdateModeDisabled && c.Spec.Tools.Mode != ToolsUpdateModeEnabled { + return trace.BadParameter("ToolsMode is not valid") + } + } return nil } diff --git a/api/types/autoupdate/config_test.go b/api/types/autoupdate/config_test.go index 2ee33dc5bf2b3..443d6f246fa56 100644 --- a/api/types/autoupdate/config_test.go +++ b/api/types/autoupdate/config_test.go @@ -41,7 +41,9 @@ func TestNewAutoUpdateConfig(t *testing.T) { { name: "success tools autoupdate disabled", spec: &autoupdate.AutoUpdateConfigSpec{ - ToolsAutoupdate: false, + Tools: &autoupdate.AutoUpdateConfigSpecTools{ + Mode: ToolsUpdateModeDisabled, + }, }, assertErr: func(t *testing.T, err error, a ...any) { require.NoError(t, err) @@ -53,14 +55,18 @@ func TestNewAutoUpdateConfig(t *testing.T) { Name: types.MetaNameAutoUpdateConfig, }, Spec: &autoupdate.AutoUpdateConfigSpec{ - ToolsAutoupdate: false, + Tools: &autoupdate.AutoUpdateConfigSpecTools{ + Mode: ToolsUpdateModeDisabled, + }, }, }, }, { name: "success tools autoupdate enabled", spec: &autoupdate.AutoUpdateConfigSpec{ - ToolsAutoupdate: true, + Tools: &autoupdate.AutoUpdateConfigSpecTools{ + Mode: ToolsUpdateModeEnabled, + }, }, assertErr: func(t *testing.T, err error, a ...any) { require.NoError(t, err) @@ -72,7 +78,9 @@ func TestNewAutoUpdateConfig(t *testing.T) { Name: types.MetaNameAutoUpdateConfig, }, Spec: &autoupdate.AutoUpdateConfigSpec{ - ToolsAutoupdate: true, + Tools: &autoupdate.AutoUpdateConfigSpecTools{ + Mode: ToolsUpdateModeEnabled, + }, }, }, }, @@ -83,6 +91,17 @@ func TestNewAutoUpdateConfig(t *testing.T) { require.ErrorContains(t, err, "Spec is nil") }, }, + { + name: "invalid tools mode", + spec: &autoupdate.AutoUpdateConfigSpec{ + Tools: &autoupdate.AutoUpdateConfigSpecTools{ + Mode: "invalid-mode", + }, + }, + assertErr: func(t *testing.T, err error, a ...any) { + require.ErrorContains(t, err, "ToolsMode is not valid") + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/api/types/autoupdate/version.go b/api/types/autoupdate/version.go index 088171a072ae3..ad2d12f265949 100644 --- a/api/types/autoupdate/version.go +++ b/api/types/autoupdate/version.go @@ -60,11 +60,13 @@ func ValidateAutoUpdateVersion(v *autoupdate.AutoUpdateVersion) error { return trace.BadParameter("Spec is nil") } - if v.Spec.ToolsVersion == "" { - return trace.BadParameter("ToolsVersion is unset") - } - if _, err := semver.NewVersion(v.Spec.ToolsVersion); err != nil { - return trace.BadParameter("ToolsVersion is not a valid semantic version") + if v.Spec.Tools != nil { + if v.Spec.Tools.TargetVersion == "" { + return trace.BadParameter("TargetVersion is unset") + } + if _, err := semver.NewVersion(v.Spec.Tools.TargetVersion); err != nil { + return trace.BadParameter("TargetVersion is not a valid semantic version") + } } return nil diff --git a/api/types/autoupdate/version_test.go b/api/types/autoupdate/version_test.go index 5fe4f167a037e..70790a204b219 100644 --- a/api/types/autoupdate/version_test.go +++ b/api/types/autoupdate/version_test.go @@ -41,7 +41,9 @@ func TestNewAutoUpdateVersion(t *testing.T) { { name: "success tools autoupdate version", spec: &autoupdate.AutoUpdateVersionSpec{ - ToolsVersion: "1.2.3-dev", + Tools: &autoupdate.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3-dev", + }, }, assertErr: func(t *testing.T, err error, a ...any) { require.NoError(t, err) @@ -53,26 +55,32 @@ func TestNewAutoUpdateVersion(t *testing.T) { Name: types.MetaNameAutoUpdateVersion, }, Spec: &autoupdate.AutoUpdateVersionSpec{ - ToolsVersion: "1.2.3-dev", + Tools: &autoupdate.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3-dev", + }, }, }, }, { name: "invalid empty tools version", spec: &autoupdate.AutoUpdateVersionSpec{ - ToolsVersion: "", + Tools: &autoupdate.AutoUpdateVersionSpecTools{ + TargetVersion: "", + }, }, assertErr: func(t *testing.T, err error, a ...any) { - require.ErrorContains(t, err, "ToolsVersion is unset") + require.ErrorContains(t, err, "TargetVersion is unset") }, }, { name: "invalid semantic tools version", spec: &autoupdate.AutoUpdateVersionSpec{ - ToolsVersion: "17-0-0", + Tools: &autoupdate.AutoUpdateVersionSpecTools{ + TargetVersion: "17-0-0", + }, }, assertErr: func(t *testing.T, err error, a ...any) { - require.ErrorContains(t, err, "ToolsVersion is not a valid semantic version") + require.ErrorContains(t, err, "TargetVersion is not a valid semantic version") }, }, { diff --git a/api/types/events/events.go b/api/types/events/events.go index dd0ca0ecf8d14..d39f492b7b75b 100644 --- a/api/types/events/events.go +++ b/api/types/events/events.go @@ -1980,3 +1980,27 @@ func (m *IntegrationUpdate) TrimToMaxSize(maxSize int) AuditEvent { func (m *IntegrationDelete) TrimToMaxSize(maxSize int) AuditEvent { return m } + +func (m *AutoUpdateConfigCreate) TrimToMaxSize(_ int) AuditEvent { + return m +} + +func (m *AutoUpdateConfigUpdate) TrimToMaxSize(_ int) AuditEvent { + return m +} + +func (m *AutoUpdateConfigDelete) TrimToMaxSize(_ int) AuditEvent { + return m +} + +func (m *AutoUpdateVersionCreate) TrimToMaxSize(_ int) AuditEvent { + return m +} + +func (m *AutoUpdateVersionUpdate) TrimToMaxSize(_ int) AuditEvent { + return m +} + +func (m *AutoUpdateVersionDelete) TrimToMaxSize(_ int) AuditEvent { + return m +} diff --git a/api/types/events/events.pb.go b/api/types/events/events.pb.go index 4bb617162401d..4eb292b80fe1f 100644 --- a/api/types/events/events.pb.go +++ b/api/types/events/events.pb.go @@ -11887,7 +11887,9 @@ type AutoUpdateConfigCreate struct { // User is a common user event metadata UserMetadata `protobuf:"bytes,3,opt,name=User,proto3,embedded=User" json:""` // ConnectionMetadata holds information about the connection - ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + // Status indicates whether the creation was successful. + Status `protobuf:"bytes,5,opt,name=Status,proto3,embedded=Status" json:""` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -11935,7 +11937,9 @@ type AutoUpdateConfigUpdate struct { // User is a common user event metadata UserMetadata `protobuf:"bytes,3,opt,name=User,proto3,embedded=User" json:""` // ConnectionMetadata holds information about the connection - ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + // ResourceMetadata is a common resource event metadata + ResourceMetadata `protobuf:"bytes,5,opt,name=Resource,proto3,embedded=Resource" json:""` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -11983,7 +11987,9 @@ type AutoUpdateConfigDelete struct { // User is a common user event metadata UserMetadata `protobuf:"bytes,3,opt,name=User,proto3,embedded=User" json:""` // ConnectionMetadata holds information about the connection - ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + // Status indicates whether the deletion was successful. + Status `protobuf:"bytes,5,opt,name=Status,proto3,embedded=Status" json:""` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -12031,7 +12037,9 @@ type AutoUpdateVersionCreate struct { // User is a common user event metadata UserMetadata `protobuf:"bytes,3,opt,name=User,proto3,embedded=User" json:""` // ConnectionMetadata holds information about the connection - ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + // Status indicates whether the creation was successful. + Status `protobuf:"bytes,5,opt,name=Status,proto3,embedded=Status" json:""` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -12079,7 +12087,9 @@ type AutoUpdateVersionUpdate struct { // User is a common user event metadata UserMetadata `protobuf:"bytes,3,opt,name=User,proto3,embedded=User" json:""` // ConnectionMetadata holds information about the connection - ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + // ResourceMetadata is a common resource event metadata + ResourceMetadata `protobuf:"bytes,5,opt,name=Resource,proto3,embedded=Resource" json:""` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -12127,7 +12137,9 @@ type AutoUpdateVersionDelete struct { // User is a common user event metadata UserMetadata `protobuf:"bytes,3,opt,name=User,proto3,embedded=User" json:""` // ConnectionMetadata holds information about the connection - ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + ConnectionMetadata `protobuf:"bytes,4,opt,name=Connection,proto3,embedded=Connection" json:""` + // Status indicates whether the deletion was successful. + Status `protobuf:"bytes,5,opt,name=Status,proto3,embedded=Status" json:""` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -12385,878 +12397,879 @@ func init() { } var fileDescriptor_007ba1c3d6266d56 = []byte{ - // 13933 bytes of a gzipped FileDescriptorProto + // 13941 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0xbd, 0x7b, 0x70, 0x24, 0xc7, 0x79, 0x18, 0x8e, 0x7d, 0xe0, 0xd5, 0x78, 0x2d, 0xfa, 0x5e, 0x43, 0xf0, 0xee, 0x96, 0x1c, 0x4a, 0xc7, 0x3b, 0xea, 0x88, 0x13, 0xef, 0x4e, 0xa4, 0x48, 0x91, 0x22, 0x17, 0x58, 0xe0, 0xb0, 0x3c, 0x3c, 0x96, 0xb3, 0xb8, 0x3b, 0x52, 0x0f, 0xae, 0x07, 0x3b, 0x7d, 0xc0, 0x10, 0xbb, 0x33, 0xab, 0x99, 0xd9, 0xc3, 0x81, 0xbf, 0x5f, 0x12, 0xcb, 0xf1, 0x33, 0x91, 0x54, 0x2a, 0xa7, 0x52, 0x76, - 0xe2, 0x54, 0xc5, 0x8f, 0x72, 0xe2, 0xb8, 0x6c, 0xcb, 0x76, 0x52, 0xb6, 0x65, 0xc7, 0x15, 0x3b, - 0x72, 0x2a, 0x74, 0x94, 0xa4, 0x6c, 0x27, 0xe5, 0x4a, 0x25, 0x0e, 0xe4, 0x28, 0xe5, 0xfc, 0x81, - 0x4a, 0xaa, 0x9c, 0x8a, 0x2a, 0x76, 0x1c, 0x27, 0x95, 0xea, 0xaf, 0x7b, 0x66, 0xba, 0xe7, 0xb1, - 0x00, 0x0e, 0xa0, 0x41, 0x08, 0xf8, 0xe7, 0x0e, 0xfb, 0x7d, 0x5f, 0x7f, 0xdd, 0xf3, 0xf5, 0xd7, - 0xef, 0xef, 0x81, 0xae, 0x78, 0xa4, 0x49, 0xda, 0xb6, 0xe3, 0x5d, 0x6b, 0x92, 0x55, 0xbd, 0xb1, - 0x79, 0xcd, 0xdb, 0x6c, 0x13, 0xf7, 0x1a, 0x79, 0x40, 0x2c, 0xcf, 0xff, 0x6f, 0xb2, 0xed, 0xd8, - 0x9e, 0x8d, 0xfb, 0xd8, 0xaf, 0x89, 0xd3, 0xab, 0xf6, 0xaa, 0x0d, 0xa0, 0x6b, 0xf4, 0x2f, 0x86, - 0x9d, 0x38, 0xbf, 0x6a, 0xdb, 0xab, 0x4d, 0x72, 0x0d, 0x7e, 0xad, 0x74, 0xee, 0x5f, 0x73, 0x3d, - 0xa7, 0xd3, 0xf0, 0x38, 0xb6, 0x18, 0xc5, 0x7a, 0x66, 0x8b, 0xb8, 0x9e, 0xde, 0x6a, 0x73, 0x82, - 0x8b, 0x51, 0x82, 0x0d, 0x47, 0x6f, 0xb7, 0x89, 0xc3, 0x2b, 0x9f, 0x78, 0x32, 0xb9, 0x9d, 0xf0, - 0x2f, 0x27, 0x79, 0x36, 0x99, 0xc4, 0x67, 0x14, 0xe1, 0xa8, 0xfe, 0x60, 0x16, 0x0d, 0x2c, 0x10, - 0x4f, 0x37, 0x74, 0x4f, 0xc7, 0xe7, 0x51, 0x6f, 0xc5, 0x32, 0xc8, 0x43, 0x25, 0xf3, 0x44, 0xe6, - 0x72, 0x6e, 0xaa, 0x6f, 0x7b, 0xab, 0x98, 0x25, 0xa6, 0xc6, 0x80, 0xf8, 0x02, 0xca, 0x2f, 0x6f, - 0xb6, 0x89, 0x92, 0x7d, 0x22, 0x73, 0x79, 0x70, 0x6a, 0x70, 0x7b, 0xab, 0xd8, 0x0b, 0xb2, 0xd0, - 0x00, 0x8c, 0x9f, 0x44, 0xd9, 0x4a, 0x59, 0xc9, 0x01, 0x72, 0x7c, 0x7b, 0xab, 0x38, 0xd2, 0x31, - 0x8d, 0xab, 0x76, 0xcb, 0xf4, 0x48, 0xab, 0xed, 0x6d, 0x6a, 0xd9, 0x4a, 0x19, 0x5f, 0x42, 0xf9, - 0x69, 0xdb, 0x20, 0x4a, 0x1e, 0x88, 0xf0, 0xf6, 0x56, 0x71, 0xb4, 0x61, 0x1b, 0x44, 0xa0, 0x02, - 0x3c, 0x7e, 0x0d, 0xe5, 0x97, 0xcd, 0x16, 0x51, 0x7a, 0x9f, 0xc8, 0x5c, 0x1e, 0xba, 0x3e, 0x31, - 0xc9, 0xa4, 0x32, 0xe9, 0x4b, 0x65, 0x72, 0xd9, 0x17, 0xdb, 0x54, 0xe1, 0xbd, 0xad, 0x62, 0xcf, - 0xf6, 0x56, 0x31, 0x4f, 0x25, 0xf9, 0xe5, 0x6f, 0x14, 0x33, 0x1a, 0x94, 0xc4, 0x2f, 0xa3, 0xa1, - 0xe9, 0x66, 0xc7, 0xf5, 0x88, 0xb3, 0xa8, 0xb7, 0x88, 0xd2, 0x07, 0x15, 0x4e, 0x6c, 0x6f, 0x15, - 0xcf, 0x36, 0x18, 0xb8, 0x6e, 0xe9, 0x2d, 0xb1, 0x62, 0x91, 0x5c, 0xfd, 0x95, 0x0c, 0x1a, 0xab, - 0x11, 0xd7, 0x35, 0x6d, 0x2b, 0x90, 0xcd, 0x87, 0xd1, 0x20, 0x07, 0x55, 0xca, 0x20, 0x9f, 0xc1, - 0xa9, 0xfe, 0xed, 0xad, 0x62, 0xce, 0x35, 0x0d, 0x2d, 0xc4, 0xe0, 0x8f, 0xa2, 0xfe, 0x7b, 0xa6, - 0xb7, 0xb6, 0x30, 0x5b, 0xe2, 0x72, 0x3a, 0xbb, 0xbd, 0x55, 0xc4, 0x1b, 0xa6, 0xb7, 0x56, 0x6f, - 0xdd, 0xd7, 0x85, 0x0a, 0x7d, 0x32, 0x3c, 0x8f, 0x0a, 0x55, 0xc7, 0x7c, 0xa0, 0x7b, 0xe4, 0x36, - 0xd9, 0xac, 0xda, 0x4d, 0xb3, 0xb1, 0xc9, 0xa5, 0xf8, 0xc4, 0xf6, 0x56, 0xf1, 0x7c, 0x9b, 0xe1, - 0xea, 0xeb, 0x64, 0xb3, 0xde, 0x06, 0xac, 0xc0, 0x24, 0x56, 0x52, 0xfd, 0x5a, 0x2f, 0x1a, 0xbe, - 0xe3, 0x12, 0x27, 0x68, 0xf7, 0x25, 0x94, 0xa7, 0xbf, 0x79, 0x93, 0x41, 0xe6, 0x1d, 0x97, 0x38, - 0xa2, 0xcc, 0x29, 0x1e, 0x5f, 0x41, 0xbd, 0xf3, 0xf6, 0xaa, 0x69, 0xf1, 0x66, 0x9f, 0xda, 0xde, - 0x2a, 0x8e, 0x35, 0x29, 0x40, 0xa0, 0x64, 0x14, 0xf8, 0x93, 0x68, 0xb8, 0xd2, 0xa2, 0x3a, 0x64, - 0x5b, 0xba, 0x67, 0x3b, 0xbc, 0xb5, 0x20, 0x5d, 0x53, 0x80, 0x0b, 0x05, 0x25, 0x7a, 0xfc, 0x12, - 0x42, 0xa5, 0x7b, 0x35, 0xcd, 0x6e, 0x92, 0x92, 0xb6, 0xc8, 0x95, 0x01, 0x4a, 0xeb, 0x1b, 0x6e, - 0xdd, 0xb1, 0x9b, 0xa4, 0xae, 0x3b, 0x62, 0xb5, 0x02, 0x35, 0x9e, 0x41, 0xa3, 0xa5, 0x46, 0x83, - 0xb8, 0xae, 0x46, 0x3e, 0xd7, 0x21, 0xae, 0xe7, 0x2a, 0xbd, 0x4f, 0xe4, 0x2e, 0x0f, 0x4e, 0x5d, - 0xd8, 0xde, 0x2a, 0x3e, 0xa6, 0x03, 0xa6, 0xee, 0x70, 0x94, 0xc0, 0x22, 0x52, 0x08, 0x4f, 0xa1, - 0x91, 0xd2, 0xbb, 0x1d, 0x87, 0x54, 0x0c, 0x62, 0x79, 0xa6, 0xb7, 0xc9, 0x35, 0xe4, 0xfc, 0xf6, - 0x56, 0x51, 0xd1, 0x29, 0xa2, 0x6e, 0x72, 0x8c, 0xc0, 0x44, 0x2e, 0x82, 0x97, 0xd0, 0xf8, 0xad, - 0xe9, 0x6a, 0x8d, 0x38, 0x0f, 0xcc, 0x06, 0x29, 0x35, 0x1a, 0x76, 0xc7, 0xf2, 0x94, 0x7e, 0xe0, - 0xf3, 0xe4, 0xf6, 0x56, 0xf1, 0xc2, 0x6a, 0xa3, 0x5d, 0x77, 0x19, 0xb6, 0xae, 0x33, 0xb4, 0xc0, - 0x2c, 0x5e, 0x16, 0x7f, 0x0a, 0x8d, 0x2c, 0x3b, 0x54, 0x0b, 0x8d, 0x32, 0xa1, 0x70, 0x65, 0x00, - 0xf4, 0xff, 0xec, 0x24, 0x9f, 0x80, 0x18, 0xd4, 0xef, 0x59, 0xd6, 0x58, 0x8f, 0x15, 0xa8, 0x1b, - 0x80, 0x13, 0x1b, 0x2b, 0xb1, 0xc2, 0x04, 0x29, 0xf4, 0xe3, 0x4d, 0x87, 0x18, 0x31, 0x6d, 0x1b, - 0x84, 0x36, 0x5f, 0xd9, 0xde, 0x2a, 0x7e, 0xd8, 0xe1, 0x34, 0xf5, 0xae, 0x6a, 0x97, 0xca, 0x0a, - 0xcf, 0xa0, 0x01, 0xaa, 0x4d, 0xb7, 0x4d, 0xcb, 0x50, 0xd0, 0x13, 0x99, 0xcb, 0xa3, 0xd7, 0x0b, - 0x7e, 0xeb, 0x7d, 0xf8, 0xd4, 0xb9, 0xed, 0xad, 0xe2, 0x29, 0xaa, 0x83, 0xf5, 0x75, 0xd3, 0x12, - 0xa7, 0x88, 0xa0, 0xa8, 0xfa, 0x27, 0x79, 0x34, 0x4a, 0x85, 0x23, 0xe8, 0x71, 0x89, 0x0e, 0x49, - 0x0a, 0xa1, 0x23, 0xd4, 0x6d, 0xeb, 0x0d, 0xc2, 0x55, 0x1a, 0xd8, 0x59, 0x3e, 0x50, 0x60, 0x17, - 0xa5, 0xc7, 0x57, 0xd0, 0x00, 0x03, 0x55, 0xca, 0x5c, 0xcb, 0x47, 0xb6, 0xb7, 0x8a, 0x83, 0x2e, - 0xc0, 0xea, 0xa6, 0xa1, 0x05, 0x68, 0xaa, 0x66, 0xec, 0xef, 0x39, 0xdb, 0xf5, 0x28, 0x73, 0xae, - 0xe4, 0xa0, 0x66, 0xbc, 0xc0, 0x1a, 0x47, 0x89, 0x6a, 0x26, 0x17, 0xc2, 0x2f, 0x22, 0xc4, 0x20, - 0x25, 0xc3, 0x70, 0xb8, 0xa6, 0x3f, 0xb6, 0xbd, 0x55, 0x3c, 0xc3, 0x59, 0xe8, 0x86, 0x21, 0x0e, - 0x13, 0x81, 0x18, 0xb7, 0xd0, 0x30, 0xfb, 0x35, 0xaf, 0xaf, 0x90, 0x26, 0x53, 0xf3, 0xa1, 0xeb, - 0x97, 0x7d, 0x69, 0xca, 0xd2, 0x99, 0x14, 0x49, 0x67, 0x2c, 0xcf, 0xd9, 0x9c, 0x2a, 0xf2, 0x99, - 0xf1, 0x1c, 0xaf, 0xaa, 0x09, 0x38, 0x71, 0x4c, 0x8a, 0x65, 0xe8, 0x84, 0x39, 0x6b, 0x3b, 0x1b, - 0xba, 0x63, 0x10, 0x63, 0x6a, 0x53, 0x9c, 0x30, 0xef, 0xfb, 0xe0, 0xfa, 0x8a, 0xa8, 0x03, 0x22, - 0x39, 0x9e, 0x46, 0x23, 0x8c, 0x5b, 0xad, 0xb3, 0x02, 0x7d, 0xdf, 0x1f, 0x93, 0x96, 0xdb, 0x59, - 0x89, 0xf6, 0xb7, 0x5c, 0x86, 0x8e, 0x49, 0x06, 0xb8, 0x4b, 0x1c, 0x3a, 0x9b, 0x82, 0xfa, 0xf3, - 0x31, 0xc9, 0x99, 0x3c, 0x60, 0x98, 0x38, 0x0f, 0x5e, 0x64, 0xe2, 0x55, 0x34, 0x1e, 0x13, 0x05, - 0x2e, 0xa0, 0xdc, 0x3a, 0xd9, 0x64, 0xea, 0xa2, 0xd1, 0x3f, 0xf1, 0x69, 0xd4, 0xfb, 0x40, 0x6f, - 0x76, 0xf8, 0x5a, 0xa6, 0xb1, 0x1f, 0x2f, 0x65, 0x3f, 0x9e, 0xa1, 0x53, 0x3f, 0x9e, 0xb6, 0x2d, - 0x8b, 0x34, 0x3c, 0x71, 0xf6, 0x7f, 0x1e, 0x0d, 0xce, 0xdb, 0x0d, 0xbd, 0x09, 0xfd, 0xc8, 0xf4, - 0x4e, 0xd9, 0xde, 0x2a, 0x9e, 0xa6, 0x1d, 0x38, 0xd9, 0xa4, 0x18, 0xa1, 0x4d, 0x21, 0x29, 0x55, - 0x00, 0x8d, 0xb4, 0x6c, 0x8f, 0x40, 0xc1, 0x6c, 0xa8, 0x00, 0x50, 0xd0, 0x01, 0x94, 0xa8, 0x00, - 0x21, 0x31, 0xbe, 0x86, 0x06, 0xaa, 0x74, 0xc1, 0x6b, 0xd8, 0x4d, 0xae, 0x7c, 0x30, 0x27, 0xc3, - 0x22, 0x28, 0x0e, 0x1a, 0x9f, 0x48, 0x9d, 0x43, 0xa3, 0xd3, 0x4d, 0x93, 0x58, 0x9e, 0xd8, 0x6a, - 0x3a, 0xa4, 0x4a, 0xab, 0xc4, 0xf2, 0xc4, 0x56, 0xc3, 0xe0, 0xd3, 0x29, 0x54, 0x6c, 0x75, 0x40, - 0xaa, 0xfe, 0xeb, 0x1c, 0x7a, 0xec, 0x76, 0x67, 0x85, 0x38, 0x16, 0xf1, 0x88, 0xcb, 0x57, 0xc6, - 0x80, 0xeb, 0x22, 0x1a, 0x8f, 0x21, 0x39, 0x77, 0x58, 0xb1, 0xd6, 0x03, 0x64, 0x9d, 0x2f, 0xb6, - 0xe2, 0xb4, 0x17, 0x2b, 0x8a, 0xe7, 0xd0, 0x58, 0x08, 0xa4, 0x8d, 0x70, 0x95, 0x2c, 0xcc, 0xe9, - 0x17, 0xb7, 0xb7, 0x8a, 0x13, 0x02, 0x37, 0xda, 0x6c, 0x51, 0x83, 0xa3, 0xc5, 0xf0, 0x6d, 0x54, - 0x08, 0x41, 0xb7, 0x1c, 0xbb, 0xd3, 0x76, 0x95, 0x1c, 0xb0, 0x2a, 0x6e, 0x6f, 0x15, 0x1f, 0x17, - 0x58, 0xad, 0x02, 0x52, 0x5c, 0x49, 0xa3, 0x05, 0xf1, 0x77, 0x67, 0x44, 0x6e, 0x7c, 0x14, 0xe6, - 0x61, 0x14, 0xbe, 0xe0, 0x8f, 0xc2, 0x54, 0x21, 0x4d, 0x46, 0x4b, 0xf2, 0x41, 0x19, 0x69, 0x46, - 0x6c, 0x50, 0xc6, 0x6a, 0x9c, 0x98, 0x46, 0x67, 0x12, 0x79, 0xed, 0x49, 0xab, 0xff, 0x28, 0x27, - 0x72, 0xa9, 0xda, 0x46, 0xd0, 0x99, 0x4b, 0x62, 0x67, 0x56, 0x6d, 0x03, 0xb6, 0x4b, 0x99, 0x70, - 0x11, 0x13, 0x1a, 0xdb, 0xb6, 0x8d, 0xe8, 0xae, 0x29, 0x5e, 0x16, 0xbf, 0x8d, 0xce, 0xc6, 0x80, - 0x6c, 0xba, 0x66, 0xda, 0x7f, 0x69, 0x7b, 0xab, 0xa8, 0x26, 0x70, 0x8d, 0xce, 0xde, 0x29, 0x5c, - 0xb0, 0x8e, 0xce, 0x09, 0x52, 0xb7, 0x2d, 0x4f, 0x37, 0x2d, 0xbe, 0xcb, 0x63, 0xa3, 0xe4, 0xe9, - 0xed, 0xad, 0xe2, 0x53, 0xa2, 0x0e, 0xfa, 0x34, 0xd1, 0xc6, 0xa7, 0xf1, 0xc1, 0x06, 0x52, 0x12, - 0x50, 0x95, 0x96, 0xbe, 0xea, 0x6f, 0x5d, 0x2f, 0x6f, 0x6f, 0x15, 0x3f, 0x94, 0x58, 0x87, 0x49, - 0xa9, 0xc4, 0xa5, 0x32, 0x8d, 0x13, 0xd6, 0x10, 0x0e, 0x71, 0x8b, 0xb6, 0x41, 0xe0, 0x1b, 0x7a, - 0x81, 0xbf, 0xba, 0xbd, 0x55, 0xbc, 0x28, 0xf0, 0xb7, 0x6c, 0x83, 0x44, 0x9b, 0x9f, 0x50, 0x5a, - 0xfd, 0x95, 0x1c, 0xba, 0x58, 0x2b, 0x2d, 0xcc, 0x57, 0x0c, 0x7f, 0x6f, 0x51, 0x75, 0xec, 0x07, - 0xa6, 0x21, 0x8c, 0xde, 0x15, 0x74, 0x2e, 0x82, 0x9a, 0x81, 0xed, 0x4c, 0xb0, 0xab, 0x85, 0x6f, - 0xf3, 0xf7, 0x2d, 0x6d, 0x4e, 0x53, 0x67, 0x7b, 0x9e, 0xba, 0xb4, 0xa5, 0x4f, 0x63, 0x44, 0xfb, - 0x28, 0x82, 0xaa, 0xad, 0xd9, 0x8e, 0xd7, 0xe8, 0x78, 0x5c, 0x09, 0xa0, 0x8f, 0x62, 0x75, 0xb8, - 0x9c, 0xa8, 0x4b, 0x15, 0x3e, 0x1f, 0xfc, 0x03, 0x19, 0x54, 0x28, 0x79, 0x9e, 0x63, 0xae, 0x74, - 0x3c, 0xb2, 0xa0, 0xb7, 0xdb, 0xa6, 0xb5, 0x0a, 0x63, 0x7d, 0xe8, 0xfa, 0xcb, 0xc1, 0x1a, 0xd9, - 0x55, 0x12, 0x93, 0xd1, 0xe2, 0xc2, 0x10, 0xd5, 0x7d, 0x54, 0xbd, 0xc5, 0x70, 0xe2, 0x10, 0x8d, - 0x96, 0xa3, 0x43, 0x34, 0x91, 0xd7, 0x9e, 0x86, 0xe8, 0x0f, 0xe6, 0xd0, 0xf9, 0xa5, 0x75, 0x4f, - 0xd7, 0x88, 0x6b, 0x77, 0x9c, 0x06, 0x71, 0xef, 0xb4, 0x0d, 0xdd, 0x23, 0xe1, 0x48, 0x2d, 0xa2, - 0xde, 0x92, 0x61, 0x10, 0x03, 0xd8, 0xf5, 0xb2, 0xf3, 0x97, 0x4e, 0x01, 0x1a, 0x83, 0xe3, 0x0f, - 0xa3, 0x7e, 0x5e, 0x06, 0xb8, 0xf7, 0x4e, 0x0d, 0x6d, 0x6f, 0x15, 0xfb, 0x3b, 0x0c, 0xa4, 0xf9, - 0x38, 0x4a, 0x56, 0x26, 0x4d, 0x42, 0xc9, 0x72, 0x21, 0x99, 0xc1, 0x40, 0x9a, 0x8f, 0xc3, 0x6f, - 0xa0, 0x51, 0x60, 0x1b, 0xb4, 0x87, 0xcf, 0x7d, 0xa7, 0x7d, 0xe9, 0x8a, 0x8d, 0x65, 0x4b, 0x13, - 0xb4, 0xa6, 0xee, 0xf8, 0x05, 0xb4, 0x08, 0x03, 0x7c, 0x0f, 0x15, 0x78, 0x23, 0x42, 0xa6, 0xbd, - 0x5d, 0x98, 0x9e, 0xd9, 0xde, 0x2a, 0x8e, 0xf3, 0xf6, 0x0b, 0x6c, 0x63, 0x4c, 0x28, 0x63, 0xde, - 0xec, 0x90, 0x71, 0xdf, 0x4e, 0x8c, 0xf9, 0x17, 0x8b, 0x8c, 0xa3, 0x4c, 0xd4, 0xb7, 0xd0, 0xb0, - 0x58, 0x10, 0x9f, 0x85, 0x33, 0x2e, 0x1b, 0x27, 0x70, 0x3a, 0x36, 0x0d, 0x38, 0xd8, 0x3e, 0x87, - 0x86, 0xca, 0xc4, 0x6d, 0x38, 0x66, 0x9b, 0xee, 0x1a, 0xb8, 0x92, 0x8f, 0x6d, 0x6f, 0x15, 0x87, - 0x8c, 0x10, 0xac, 0x89, 0x34, 0xea, 0xff, 0xcc, 0xa0, 0xb3, 0x94, 0x77, 0xc9, 0x75, 0xcd, 0x55, - 0xab, 0x25, 0x2e, 0xdb, 0x57, 0x51, 0x5f, 0x0d, 0xea, 0xe3, 0x35, 0x9d, 0xde, 0xde, 0x2a, 0x16, - 0x58, 0x0b, 0x04, 0x3d, 0xe4, 0x34, 0xc1, 0x01, 0x2f, 0xbb, 0xc3, 0x01, 0x8f, 0x6e, 0x69, 0x3d, - 0xdd, 0xf1, 0x4c, 0x6b, 0xb5, 0xe6, 0xe9, 0x5e, 0xc7, 0x95, 0xb6, 0xb4, 0x1c, 0x53, 0x77, 0x01, - 0x25, 0x6d, 0x69, 0xa5, 0x42, 0xf8, 0x55, 0x34, 0x3c, 0x63, 0x19, 0x21, 0x13, 0x36, 0x21, 0x3e, - 0x4e, 0x77, 0x9a, 0x04, 0xe0, 0x71, 0x16, 0x52, 0x01, 0xf5, 0x67, 0x33, 0x48, 0x61, 0xa7, 0xb1, - 0x79, 0xd3, 0xf5, 0x16, 0x48, 0x6b, 0x45, 0x98, 0x9d, 0x66, 0xfd, 0xe3, 0x1d, 0xc5, 0x09, 0x6b, - 0x11, 0x6c, 0x05, 0xf8, 0xf1, 0xae, 0x69, 0xba, 0x5e, 0x74, 0x32, 0x8c, 0x94, 0xc2, 0x15, 0xd4, - 0xcf, 0x38, 0xb3, 0xbd, 0xc4, 0xd0, 0x75, 0xc5, 0x57, 0x84, 0x68, 0xd5, 0x4c, 0x19, 0x5a, 0x8c, - 0x58, 0x3c, 0x9f, 0xf3, 0xf2, 0xea, 0xcf, 0x67, 0x51, 0x21, 0x5a, 0x08, 0xdf, 0x43, 0x03, 0xaf, - 0xdb, 0xa6, 0x45, 0x8c, 0x25, 0x0b, 0x5a, 0xd8, 0xfd, 0x96, 0xc2, 0xdf, 0x8b, 0x9f, 0x7a, 0x07, - 0xca, 0xd4, 0xc5, 0x1d, 0x2c, 0x5c, 0x5a, 0x04, 0xcc, 0xf0, 0xa7, 0xd0, 0x20, 0xdd, 0x03, 0x3e, - 0x00, 0xce, 0xd9, 0x1d, 0x39, 0x3f, 0xc1, 0x39, 0x9f, 0x76, 0x58, 0xa1, 0x38, 0xeb, 0x90, 0x1d, - 0xd5, 0x2b, 0x8d, 0xe8, 0xae, 0x6d, 0xf1, 0x9e, 0x07, 0xbd, 0x72, 0x00, 0x22, 0xea, 0x15, 0xa3, - 0xa1, 0x5b, 0x57, 0xf6, 0xb1, 0xd0, 0x0d, 0xc2, 0xd9, 0x85, 0xc9, 0x2a, 0xda, 0x03, 0x02, 0xb1, - 0xfa, 0xbd, 0x59, 0xf4, 0x6c, 0x28, 0x32, 0x8d, 0x3c, 0x30, 0xc9, 0x06, 0x17, 0xe7, 0x9a, 0xd9, - 0xe6, 0x87, 0x47, 0xaa, 0xf2, 0xee, 0xf4, 0x9a, 0x6e, 0xad, 0x12, 0x03, 0x5f, 0x41, 0xbd, 0xf4, - 0x84, 0xef, 0x2a, 0x19, 0xd8, 0xae, 0xc1, 0x74, 0xe2, 0x50, 0x80, 0x78, 0xfb, 0x00, 0x14, 0xd8, - 0x46, 0x7d, 0xcb, 0x8e, 0x6e, 0x7a, 0x7e, 0xcf, 0x96, 0xe2, 0x3d, 0xbb, 0x8b, 0x1a, 0x27, 0x19, - 0x0f, 0x36, 0xe7, 0x83, 0x20, 0x3c, 0x00, 0x88, 0x82, 0x60, 0x24, 0x13, 0x2f, 0xa2, 0x21, 0x81, - 0x78, 0x4f, 0x93, 0xfa, 0x57, 0xf3, 0xa2, 0xae, 0xfb, 0xcd, 0xe2, 0xba, 0x7e, 0x8d, 0xea, 0xa8, - 0xeb, 0xd2, 0x5d, 0x05, 0x53, 0x72, 0xae, 0x89, 0x00, 0x92, 0x35, 0x11, 0x40, 0xf8, 0x06, 0x1a, - 0x60, 0x2c, 0x82, 0xf3, 0x2b, 0x9c, 0x7d, 0x1d, 0x80, 0xc9, 0x4b, 0x73, 0x40, 0x88, 0x7f, 0x3a, - 0x83, 0x2e, 0x74, 0x95, 0x04, 0x28, 0xc3, 0xd0, 0xf5, 0x8f, 0x3d, 0x92, 0x18, 0xa7, 0x9e, 0xdd, - 0xde, 0x2a, 0x5e, 0x69, 0x05, 0x24, 0x75, 0x47, 0xa0, 0xa9, 0x37, 0x18, 0x91, 0xd0, 0xae, 0xee, - 0x4d, 0xa1, 0x9b, 0x47, 0x56, 0xe9, 0x2c, 0xdc, 0xe1, 0x58, 0x8d, 0x4d, 0xbf, 0x91, 0xf9, 0x70, - 0xf3, 0xc8, 0xbf, 0xf7, 0xbe, 0x4f, 0x92, 0x50, 0x4d, 0x0a, 0x17, 0xdc, 0x40, 0xe7, 0x18, 0xa6, - 0xac, 0x6f, 0x2e, 0xdd, 0x5f, 0xb0, 0x2d, 0x6f, 0xcd, 0xaf, 0xa0, 0x57, 0xbc, 0x04, 0x81, 0x0a, - 0x0c, 0x7d, 0xb3, 0x6e, 0xdf, 0xaf, 0xb7, 0x28, 0x55, 0x42, 0x1d, 0x69, 0x9c, 0xe8, 0x44, 0xcb, - 0xc7, 0x9c, 0x3f, 0x05, 0xf5, 0x85, 0x57, 0x54, 0xfe, 0x38, 0x8d, 0x4f, 0x38, 0x91, 0x42, 0x6a, - 0x05, 0x0d, 0xcf, 0xdb, 0x8d, 0xf5, 0x40, 0x5d, 0x5e, 0x44, 0x7d, 0xcb, 0xba, 0xb3, 0x4a, 0x3c, - 0x90, 0xc5, 0xd0, 0xf5, 0xf1, 0x49, 0x76, 0xed, 0x4b, 0x89, 0x18, 0x62, 0x6a, 0x94, 0xcf, 0x06, - 0x7d, 0x1e, 0xfc, 0xd6, 0x78, 0x01, 0xf5, 0x1b, 0xbd, 0x68, 0x98, 0x5f, 0x51, 0xc2, 0x6c, 0x8e, - 0x5f, 0x0a, 0x2f, 0x7d, 0xf9, 0xf4, 0x15, 0x5c, 0xd3, 0x04, 0xd7, 0x4b, 0xc3, 0x94, 0xd9, 0xef, - 0x6c, 0x15, 0x33, 0xdb, 0x5b, 0xc5, 0x1e, 0x6d, 0x40, 0x38, 0x54, 0x86, 0xeb, 0x8d, 0xb0, 0xc0, - 0x8a, 0x97, 0x8e, 0x91, 0xb2, 0x6c, 0xfd, 0x79, 0x15, 0xf5, 0xf3, 0x36, 0x70, 0x8d, 0x3b, 0x17, - 0xde, 0x65, 0x48, 0x57, 0xad, 0x91, 0xd2, 0x7e, 0x29, 0xfc, 0x32, 0xea, 0x63, 0x67, 0x7b, 0x2e, - 0x80, 0xb3, 0xc9, 0x77, 0x21, 0x91, 0xe2, 0xbc, 0x0c, 0x9e, 0x43, 0x28, 0x3c, 0xd7, 0x07, 0x37, - 0xcb, 0x9c, 0x43, 0xfc, 0xc4, 0x1f, 0xe1, 0x22, 0x94, 0xc5, 0xcf, 0xa3, 0xe1, 0x65, 0xe2, 0xb4, - 0x4c, 0x4b, 0x6f, 0xd6, 0xcc, 0x77, 0xfd, 0xcb, 0x65, 0x58, 0x78, 0x5d, 0xf3, 0x5d, 0x71, 0xe4, - 0x4a, 0x74, 0xf8, 0xb3, 0x49, 0xe7, 0xe6, 0x7e, 0x68, 0xc8, 0x93, 0x3b, 0x1e, 0x28, 0x23, 0xed, - 0x49, 0x38, 0x46, 0xbf, 0x81, 0x46, 0xa4, 0x23, 0x13, 0xbf, 0x3d, 0xbc, 0x10, 0x67, 0x2d, 0x9c, - 0xff, 0x22, 0x6c, 0x65, 0x0e, 0x54, 0x93, 0x2b, 0x96, 0xe9, 0x99, 0x7a, 0x73, 0xda, 0x6e, 0xb5, - 0x74, 0xcb, 0x50, 0x06, 0x43, 0x4d, 0x36, 0x19, 0xa6, 0xde, 0x60, 0x28, 0x51, 0x93, 0xe5, 0x42, - 0xf4, 0x58, 0xce, 0xfb, 0x50, 0x23, 0x0d, 0xdb, 0xa1, 0x7b, 0x01, 0xb8, 0x1c, 0xe4, 0xc7, 0x72, - 0x97, 0xe1, 0xea, 0x8e, 0x8f, 0x14, 0x37, 0xdb, 0xd1, 0x82, 0xaf, 0xe7, 0x07, 0x86, 0x0a, 0xc3, - 0xd1, 0xfb, 0x5c, 0xf5, 0x1f, 0xe4, 0xd0, 0x10, 0x27, 0xa5, 0x4b, 0xe9, 0x89, 0x82, 0xef, 0x47, - 0xc1, 0x13, 0x15, 0xb5, 0xef, 0xa0, 0x14, 0x55, 0xfd, 0x42, 0x36, 0x98, 0x8d, 0xaa, 0x8e, 0x69, - 0xed, 0x6f, 0x36, 0xba, 0x84, 0xd0, 0xf4, 0x5a, 0xc7, 0x5a, 0x67, 0xef, 0x56, 0xd9, 0xf0, 0xdd, - 0xaa, 0x61, 0x6a, 0x02, 0x06, 0x5f, 0x40, 0xf9, 0x32, 0xe5, 0x4f, 0x7b, 0x66, 0x78, 0x6a, 0xf0, - 0x3d, 0xc6, 0x29, 0xf3, 0xac, 0x06, 0x60, 0x7a, 0xb8, 0x9a, 0xda, 0xf4, 0x08, 0xdb, 0xce, 0xe6, - 0xd8, 0xe1, 0x6a, 0x85, 0x02, 0x34, 0x06, 0xc7, 0x37, 0xd1, 0x78, 0x99, 0x34, 0xf5, 0xcd, 0x05, - 0xb3, 0xd9, 0x34, 0x5d, 0xd2, 0xb0, 0x2d, 0xc3, 0x05, 0x21, 0xf3, 0xea, 0x5a, 0xae, 0x16, 0x27, - 0xc0, 0x2a, 0xea, 0x5b, 0xba, 0x7f, 0xdf, 0x25, 0x1e, 0x88, 0x2f, 0x37, 0x85, 0xe8, 0xe4, 0x6c, - 0x03, 0x44, 0xe3, 0x18, 0xf5, 0x2b, 0x19, 0x7a, 0x7a, 0x71, 0xd7, 0x3d, 0xbb, 0x1d, 0x68, 0xf9, - 0xbe, 0x44, 0x72, 0x25, 0xdc, 0x57, 0x64, 0xe1, 0x6b, 0xc7, 0xf8, 0xd7, 0xf6, 0xf3, 0xbd, 0x45, - 0xb8, 0xa3, 0x48, 0xfc, 0xaa, 0xdc, 0x0e, 0x5f, 0xa5, 0xfe, 0x71, 0x16, 0x9d, 0xe3, 0x2d, 0x9e, - 0x6e, 0x9a, 0xed, 0x15, 0x5b, 0x77, 0x0c, 0x8d, 0x34, 0x88, 0xf9, 0x80, 0x1c, 0xcd, 0x81, 0x27, - 0x0f, 0x9d, 0xfc, 0x3e, 0x86, 0xce, 0x75, 0x38, 0x08, 0x52, 0xc9, 0xc0, 0x85, 0x2f, 0xdb, 0x54, - 0x14, 0xb6, 0xb7, 0x8a, 0xc3, 0x06, 0x03, 0xc3, 0x95, 0xbf, 0x26, 0x12, 0x51, 0x25, 0x99, 0x27, - 0xd6, 0xaa, 0xb7, 0x06, 0x4a, 0xd2, 0xcb, 0x94, 0xa4, 0x09, 0x10, 0x8d, 0x63, 0xd4, 0xff, 0x96, - 0x45, 0xa7, 0xa3, 0x22, 0xaf, 0x11, 0xcb, 0x38, 0x91, 0xf7, 0xfb, 0x23, 0xef, 0x6f, 0xe5, 0xd0, - 0xe3, 0xbc, 0x4c, 0x6d, 0x4d, 0x77, 0x88, 0x51, 0x36, 0x1d, 0xd2, 0xf0, 0x6c, 0x67, 0xf3, 0x08, - 0x6f, 0xa0, 0x0e, 0x4e, 0xec, 0x37, 0x51, 0x1f, 0x3f, 0xfe, 0xb3, 0x75, 0x66, 0x34, 0x68, 0x09, - 0x40, 0x63, 0x2b, 0x14, 0xbb, 0x3a, 0x88, 0x74, 0x56, 0xdf, 0x6e, 0x3a, 0xeb, 0xe3, 0x68, 0x24, - 0x10, 0x3d, 0x1c, 0x44, 0xfb, 0xc3, 0xdd, 0x96, 0xe1, 0x23, 0xe0, 0x2c, 0xaa, 0xc9, 0x84, 0x50, - 0x9b, 0x0f, 0xa8, 0x94, 0x61, 0x37, 0x34, 0xc2, 0x6b, 0x0b, 0xca, 0x99, 0x86, 0x26, 0x12, 0xa9, - 0x5b, 0x79, 0x34, 0x91, 0xdc, 0xed, 0x1a, 0xd1, 0x8d, 0x93, 0x5e, 0xff, 0xb6, 0xec, 0x75, 0xfc, - 0x24, 0xca, 0x57, 0x75, 0x6f, 0x8d, 0xbf, 0x83, 0xc3, 0x9b, 0xf0, 0x7d, 0xb3, 0x49, 0xea, 0x6d, - 0xdd, 0x5b, 0xd3, 0x00, 0x25, 0xcc, 0x19, 0x08, 0x38, 0x26, 0xcc, 0x19, 0xc2, 0x62, 0x3f, 0xf4, - 0x44, 0xe6, 0x72, 0x3e, 0x71, 0xb1, 0xff, 0x46, 0x3e, 0x6d, 0x5e, 0xb9, 0xe7, 0x98, 0x1e, 0x39, - 0xd1, 0xb0, 0x13, 0x0d, 0xdb, 0xa7, 0x86, 0xfd, 0x5e, 0x16, 0x8d, 0x04, 0x87, 0xa6, 0x77, 0x48, - 0xe3, 0x70, 0xd6, 0xaa, 0xf0, 0x28, 0x93, 0xdb, 0xf7, 0x51, 0x66, 0x3f, 0x0a, 0xa5, 0x06, 0x57, - 0x9e, 0x6c, 0x6b, 0x00, 0x12, 0x63, 0x57, 0x9e, 0xc1, 0x45, 0xe7, 0x93, 0xa8, 0x7f, 0x41, 0x7f, - 0x68, 0xb6, 0x3a, 0x2d, 0xbe, 0x4b, 0x07, 0xbb, 0xae, 0x96, 0xfe, 0x50, 0xf3, 0xe1, 0xea, 0xbf, - 0xcd, 0xa0, 0x51, 0x2e, 0x54, 0xce, 0x7c, 0x5f, 0x52, 0x0d, 0xa5, 0x93, 0xdd, 0xb7, 0x74, 0x72, - 0x8f, 0x2e, 0x1d, 0xf5, 0xef, 0xe4, 0x90, 0x32, 0x6b, 0x36, 0xc9, 0xb2, 0xa3, 0x5b, 0xee, 0x7d, - 0xe2, 0xf0, 0xe3, 0xf4, 0x0c, 0x65, 0xb5, 0xaf, 0x0f, 0x14, 0xa6, 0x94, 0xec, 0x23, 0x4d, 0x29, - 0x1f, 0x41, 0x83, 0xbc, 0x31, 0x81, 0x4d, 0x21, 0x8c, 0x1a, 0xc7, 0x07, 0x6a, 0x21, 0x9e, 0x12, - 0x97, 0xda, 0x6d, 0xc7, 0x7e, 0x40, 0x1c, 0xf6, 0x4a, 0xc5, 0x89, 0x75, 0x1f, 0xa8, 0x85, 0x78, - 0x81, 0x33, 0xf1, 0xf7, 0x8b, 0x22, 0x67, 0xe2, 0x68, 0x21, 0x1e, 0x5f, 0x46, 0x03, 0xf3, 0x76, - 0x43, 0x07, 0x41, 0xb3, 0x69, 0x65, 0x78, 0x7b, 0xab, 0x38, 0xd0, 0xe4, 0x30, 0x2d, 0xc0, 0x52, - 0xca, 0xb2, 0xbd, 0x61, 0x35, 0x6d, 0x9d, 0x19, 0xbf, 0x0c, 0x30, 0x4a, 0x83, 0xc3, 0xb4, 0x00, - 0x4b, 0x29, 0xa9, 0xcc, 0xc1, 0xa8, 0x68, 0x20, 0xe4, 0x79, 0x9f, 0xc3, 0xb4, 0x00, 0xab, 0x7e, - 0x25, 0x4f, 0xb5, 0xd7, 0x35, 0xdf, 0x3d, 0xf6, 0xeb, 0x42, 0x38, 0x60, 0x7a, 0x1f, 0x61, 0xc0, - 0x1c, 0x9b, 0x0b, 0x3b, 0xf5, 0x4f, 0xfa, 0x11, 0xe2, 0xd2, 0x9f, 0x39, 0x39, 0x1c, 0xee, 0x4f, - 0x6b, 0xca, 0x68, 0x7c, 0xc6, 0x5a, 0xd3, 0xad, 0x06, 0x31, 0xc2, 0x6b, 0xcb, 0x3e, 0x18, 0xda, - 0x60, 0xd3, 0x4b, 0x38, 0x32, 0xbc, 0xb7, 0xd4, 0xe2, 0x05, 0xf0, 0x73, 0x68, 0xa8, 0x62, 0x79, - 0xc4, 0xd1, 0x1b, 0x9e, 0xf9, 0x80, 0xf0, 0xa9, 0x01, 0x5e, 0x86, 0xcd, 0x10, 0xac, 0x89, 0x34, - 0xf8, 0x26, 0x1a, 0xae, 0xea, 0x8e, 0x67, 0x36, 0xcc, 0xb6, 0x6e, 0x79, 0xae, 0x32, 0x00, 0x33, - 0x1a, 0xec, 0x30, 0xda, 0x02, 0x5c, 0x93, 0xa8, 0xf0, 0x67, 0xd1, 0x20, 0x1c, 0x4d, 0xc1, 0x70, - 0x7a, 0x70, 0xc7, 0x87, 0xc3, 0xa7, 0x42, 0xf3, 0x40, 0x76, 0xfb, 0x0a, 0x2f, 0xc0, 0xd1, 0xb7, - 0xc3, 0x80, 0x23, 0x7e, 0x13, 0xf5, 0xcf, 0x58, 0x06, 0x30, 0x47, 0x3b, 0x32, 0x57, 0x39, 0xf3, - 0xb3, 0x21, 0x73, 0xbb, 0x1d, 0xe1, 0xed, 0xb3, 0x4b, 0x1e, 0x65, 0x43, 0xef, 0xdf, 0x28, 0x1b, - 0x7e, 0x1f, 0xae, 0xc5, 0x47, 0x0e, 0xea, 0x5a, 0x7c, 0xf4, 0x11, 0xaf, 0xc5, 0xd5, 0x77, 0xd1, - 0xd0, 0x54, 0x75, 0x36, 0x18, 0xbd, 0x8f, 0xa1, 0x5c, 0x95, 0x5b, 0x2a, 0xe4, 0xd9, 0x7e, 0xa6, - 0x6d, 0x1a, 0x1a, 0x85, 0xe1, 0x2b, 0x68, 0x60, 0x1a, 0xcc, 0xdf, 0xf8, 0x2b, 0x62, 0x9e, 0xad, - 0x7f, 0x0d, 0x80, 0x81, 0x15, 0xac, 0x8f, 0xc6, 0x1f, 0x46, 0xfd, 0x55, 0xc7, 0x5e, 0x75, 0xf4, - 0x16, 0x5f, 0x83, 0xc1, 0x54, 0xa4, 0xcd, 0x40, 0x9a, 0x8f, 0x53, 0xff, 0x46, 0xc6, 0xdf, 0xb6, - 0xd3, 0x12, 0xb5, 0x0e, 0x5c, 0xcd, 0x43, 0xdd, 0x03, 0xac, 0x84, 0xcb, 0x40, 0x9a, 0x8f, 0xc3, - 0x57, 0x50, 0xef, 0x8c, 0xe3, 0xd8, 0x8e, 0x68, 0x6c, 0x4e, 0x28, 0x40, 0x7c, 0xee, 0x05, 0x0a, - 0xfc, 0x02, 0x1a, 0x62, 0x73, 0x0e, 0xbb, 0xd1, 0xcc, 0x75, 0x7b, 0x29, 0x15, 0x29, 0xd5, 0xaf, - 0xe5, 0x84, 0x3d, 0x1b, 0x93, 0xf8, 0x31, 0x7c, 0x15, 0xb8, 0x81, 0x72, 0x53, 0xd5, 0x59, 0x3e, - 0x01, 0x9e, 0xf2, 0x8b, 0x0a, 0xaa, 0x12, 0x29, 0x47, 0xa9, 0xf1, 0x79, 0x94, 0xaf, 0x52, 0xf5, - 0xe9, 0x03, 0xf5, 0x18, 0xd8, 0xde, 0x2a, 0xe6, 0xdb, 0x54, 0x7f, 0x00, 0x0a, 0x58, 0x7a, 0x98, - 0x61, 0x27, 0x26, 0x86, 0x0d, 0xcf, 0x31, 0xe7, 0x51, 0xbe, 0xe4, 0xac, 0x3e, 0xe0, 0xb3, 0x16, - 0x60, 0x75, 0x67, 0xf5, 0x81, 0x06, 0x50, 0x7c, 0x0d, 0x21, 0x8d, 0x78, 0x1d, 0xc7, 0x02, 0x3f, - 0x90, 0x41, 0xb8, 0x7f, 0x83, 0xd9, 0xd0, 0x01, 0x68, 0xbd, 0x61, 0x1b, 0x44, 0x13, 0x48, 0xd4, - 0x9f, 0x0c, 0x1f, 0x76, 0xca, 0xa6, 0xbb, 0x7e, 0xd2, 0x85, 0x7b, 0xe8, 0x42, 0x9d, 0x5f, 0x71, - 0xc6, 0x3b, 0xa9, 0x88, 0x7a, 0x67, 0x9b, 0xfa, 0xaa, 0x0b, 0x7d, 0xc8, 0x6d, 0xc9, 0xee, 0x53, - 0x80, 0xc6, 0xe0, 0x91, 0x7e, 0x1a, 0xd8, 0xb9, 0x9f, 0x7e, 0xa8, 0x37, 0x18, 0x6d, 0x8b, 0xc4, - 0xdb, 0xb0, 0x9d, 0x93, 0xae, 0xda, 0x6d, 0x57, 0x5d, 0x42, 0xfd, 0x35, 0xa7, 0x21, 0x5c, 0x5d, - 0xc0, 0x79, 0xc0, 0x75, 0x1a, 0xec, 0xda, 0xc2, 0x47, 0x52, 0xba, 0xb2, 0xeb, 0x01, 0x5d, 0x7f, - 0x48, 0x67, 0xb8, 0x1e, 0xa7, 0xe3, 0x48, 0x4e, 0x57, 0xb5, 0x1d, 0x8f, 0x77, 0x5c, 0x40, 0xd7, - 0xb6, 0x1d, 0x4f, 0xf3, 0x91, 0xf8, 0x23, 0x08, 0x2d, 0x4f, 0x57, 0x7d, 0x63, 0xfb, 0xc1, 0xd0, - 0x16, 0x90, 0x5b, 0xd9, 0x6b, 0x02, 0x1a, 0x2f, 0xa3, 0xc1, 0xa5, 0x36, 0x71, 0xd8, 0x51, 0x88, - 0x79, 0x76, 0x3c, 0x1d, 0x11, 0x2d, 0xef, 0xf7, 0x49, 0xfe, 0x7f, 0x40, 0xce, 0xd6, 0x17, 0xdb, - 0xff, 0xa9, 0x85, 0x8c, 0xf0, 0x0b, 0xa8, 0xaf, 0xc4, 0xf6, 0x79, 0x43, 0xc0, 0x32, 0x10, 0x19, - 0x1c, 0x41, 0x19, 0x8a, 0x9d, 0xd9, 0x75, 0xf8, 0x5b, 0xe3, 0xe4, 0xea, 0x15, 0x54, 0x88, 0x56, - 0x83, 0x87, 0x50, 0xff, 0xf4, 0xd2, 0xe2, 0xe2, 0xcc, 0xf4, 0x72, 0xa1, 0x07, 0x0f, 0xa0, 0x7c, - 0x6d, 0x66, 0xb1, 0x5c, 0xc8, 0xa8, 0x3f, 0x23, 0xcc, 0x20, 0x54, 0xb5, 0x4e, 0x9e, 0x86, 0xf7, - 0xf5, 0xde, 0x52, 0x80, 0xf7, 0x50, 0xb8, 0x31, 0x68, 0x99, 0x9e, 0x47, 0x0c, 0xbe, 0x4a, 0xc0, - 0x7b, 0xa1, 0xf7, 0x50, 0x8b, 0xe1, 0xf1, 0x55, 0x34, 0x02, 0x30, 0xfe, 0x44, 0xc8, 0xce, 0xc7, - 0xbc, 0x80, 0xf3, 0x50, 0x93, 0x91, 0xea, 0xd7, 0xc3, 0xd7, 0xe1, 0x79, 0xa2, 0x1f, 0xd5, 0x17, - 0xc5, 0x0f, 0x48, 0x7f, 0xa9, 0x7f, 0x9e, 0x67, 0x2e, 0x20, 0xcc, 0x71, 0xef, 0x30, 0x44, 0x19, - 0x5e, 0xe9, 0xe6, 0xf6, 0x70, 0xa5, 0x7b, 0x15, 0xf5, 0x2d, 0x10, 0x6f, 0xcd, 0xf6, 0x0d, 0xbf, - 0xc0, 0x42, 0xaf, 0x05, 0x10, 0xd1, 0x42, 0x8f, 0xd1, 0xe0, 0x75, 0x84, 0x7d, 0xaf, 0xbc, 0xc0, - 0x10, 0xdb, 0xbf, 0x42, 0x3e, 0x17, 0x3b, 0xa7, 0xd4, 0xc0, 0x25, 0x17, 0x6c, 0xec, 0x4f, 0x07, - 0x86, 0xde, 0x82, 0x25, 0xd6, 0x9f, 0x6d, 0x15, 0xfb, 0x18, 0x8d, 0x96, 0xc0, 0x16, 0xbf, 0x81, - 0x06, 0x17, 0x66, 0x4b, 0xdc, 0x43, 0x8f, 0x59, 0x45, 0x3c, 0x16, 0x48, 0xd1, 0x47, 0x04, 0x22, - 0x01, 0x7f, 0x9b, 0xd6, 0x7d, 0x3d, 0xee, 0xa0, 0x17, 0x72, 0xa1, 0xda, 0xc2, 0x3c, 0x77, 0xf8, - 0xed, 0x42, 0xa0, 0x2d, 0xb2, 0x3f, 0x4f, 0x54, 0x56, 0x0c, 0x1b, 0xd1, 0x96, 0x81, 0x7d, 0x8c, - 0xee, 0x25, 0x34, 0x5e, 0x6a, 0xb7, 0x9b, 0x26, 0x31, 0x40, 0x5f, 0xb4, 0x4e, 0x93, 0xb8, 0xdc, - 0xe4, 0x07, 0x9c, 0x41, 0x74, 0x86, 0xac, 0x83, 0x5f, 0x68, 0xdd, 0xe9, 0xc8, 0xf6, 0x99, 0xf1, - 0xb2, 0xea, 0x7f, 0xcd, 0xa0, 0x82, 0x6f, 0x3c, 0x2d, 0x7a, 0xa4, 0x0a, 0x96, 0xbd, 0x70, 0x0d, - 0x13, 0xb1, 0x25, 0x05, 0x3c, 0xae, 0xa1, 0xfe, 0x99, 0x87, 0x6d, 0xd3, 0x21, 0xee, 0x2e, 0x0c, - 0x61, 0x2f, 0xf0, 0x23, 0xe7, 0x38, 0x61, 0x45, 0x62, 0xa7, 0x4d, 0x06, 0x06, 0x97, 0x28, 0x66, - 0x3e, 0x3e, 0xe5, 0xbb, 0xd9, 0x32, 0x97, 0x28, 0x6e, 0x66, 0x2e, 0xf9, 0xb8, 0x85, 0xa4, 0xf8, - 0x29, 0x94, 0x5b, 0x5e, 0x9e, 0xe7, 0xda, 0x08, 0xee, 0xcd, 0x9e, 0x27, 0xfa, 0x7c, 0x51, 0xac, - 0xfa, 0x07, 0x59, 0x84, 0xa8, 0xd2, 0x4f, 0x3b, 0x44, 0x3f, 0xa4, 0xc7, 0x9c, 0x29, 0x34, 0xe0, - 0x0b, 0x9c, 0x0f, 0xb8, 0xc0, 0xf2, 0x39, 0xda, 0x11, 0xd1, 0xba, 0x03, 0x2b, 0xf7, 0xa2, 0x6f, - 0x8c, 0xcb, 0xee, 0x52, 0x61, 0x77, 0x08, 0xc6, 0xb8, 0xbe, 0x09, 0xee, 0x47, 0xd0, 0x20, 0xd7, - 0x1a, 0x5b, 0xba, 0x43, 0x6d, 0xf8, 0x40, 0x2d, 0xc4, 0x47, 0xd4, 0xb3, 0x6f, 0x1f, 0x93, 0xd9, - 0x97, 0xb8, 0x78, 0x99, 0x99, 0xfe, 0x91, 0x15, 0xef, 0x81, 0x5d, 0x70, 0xa9, 0xbf, 0x97, 0x41, - 0x98, 0x36, 0xab, 0xaa, 0xbb, 0xee, 0x86, 0xed, 0x18, 0xcc, 0x02, 0xf5, 0x50, 0x04, 0x73, 0x70, - 0x8f, 0x12, 0x5f, 0x1b, 0x40, 0xa7, 0x24, 0xeb, 0xbe, 0x23, 0x3e, 0x9a, 0xae, 0xc8, 0xa3, 0xa9, - 0x9b, 0x69, 0xfb, 0x87, 0xc4, 0x57, 0x8f, 0x5e, 0xc9, 0xcb, 0x44, 0x78, 0xee, 0x78, 0x16, 0x0d, - 0xf3, 0x1f, 0x74, 0xb1, 0xf4, 0xaf, 0xb3, 0x61, 0x94, 0xba, 0x14, 0xa0, 0x49, 0x68, 0xfc, 0x31, - 0x34, 0x48, 0x07, 0xcc, 0x2a, 0xb8, 0xea, 0xf7, 0x87, 0x66, 0xe3, 0x86, 0x0f, 0x14, 0x27, 0xbc, - 0x80, 0x52, 0x70, 0x16, 0x18, 0xd8, 0x85, 0xb3, 0xc0, 0xdb, 0x68, 0xa8, 0x64, 0x59, 0xb6, 0x07, - 0x3b, 0x71, 0x97, 0xdf, 0x3f, 0xa6, 0x2e, 0xbd, 0x4f, 0x81, 0x07, 0x6c, 0x48, 0x9f, 0xb8, 0xf6, - 0x8a, 0x0c, 0xf1, 0x75, 0xdf, 0xf4, 0x9d, 0x38, 0xdc, 0x74, 0x14, 0xee, 0x60, 0x1d, 0x0e, 0x8b, - 0x5b, 0xbe, 0x43, 0xe7, 0x8d, 0x54, 0x1d, 0xbb, 0x6d, 0xbb, 0xc4, 0x60, 0x82, 0x1a, 0x0a, 0xfd, - 0x89, 0xdb, 0x1c, 0x01, 0xce, 0x2a, 0x92, 0xdb, 0xbc, 0x54, 0x04, 0xdf, 0x47, 0xa7, 0xfd, 0xd7, - 0xa0, 0xc0, 0x2d, 0xa8, 0x52, 0x76, 0x95, 0x61, 0x70, 0x3d, 0xc0, 0x51, 0x65, 0xa8, 0x94, 0xa7, - 0x2e, 0xfa, 0x77, 0x9f, 0xbe, 0x5f, 0x51, 0xdd, 0x34, 0xc4, 0xae, 0x4e, 0xe4, 0x87, 0xbf, 0x03, - 0x0d, 0x2d, 0xe8, 0x0f, 0xcb, 0x1d, 0x7e, 0xc0, 0x1a, 0xd9, 0xfd, 0x15, 0x6b, 0x4b, 0x7f, 0x58, - 0x37, 0x78, 0xb9, 0xc8, 0xa2, 0x27, 0xb2, 0xc4, 0x75, 0x74, 0xb6, 0xea, 0xd8, 0x2d, 0xdb, 0x23, - 0x46, 0xc4, 0xc3, 0x66, 0x2c, 0x74, 0xc9, 0x6b, 0x73, 0x8a, 0x7a, 0x17, 0x57, 0x9b, 0x14, 0x36, - 0xb8, 0x85, 0xc6, 0x4a, 0xae, 0xdb, 0x69, 0x91, 0xf0, 0x1a, 0xba, 0xb0, 0xe3, 0x67, 0x3c, 0xcd, - 0x4d, 0x13, 0x1f, 0xd7, 0xa1, 0x28, 0xbb, 0x85, 0xae, 0x7b, 0xa6, 0x58, 0x23, 0x7c, 0x4b, 0x94, - 0xf7, 0xeb, 0xf9, 0x81, 0xd1, 0xc2, 0x98, 0x76, 0x2e, 0xde, 0x98, 0x65, 0xd3, 0x6b, 0x12, 0xf5, - 0xd7, 0x33, 0x08, 0x85, 0x02, 0xc6, 0xcf, 0xca, 0xf1, 0x40, 0x32, 0xe1, 0x6d, 0x26, 0x77, 0x51, - 0x96, 0x02, 0x80, 0xe0, 0xf3, 0x28, 0x0f, 0x6e, 0xec, 0xd9, 0xf0, 0xf6, 0x64, 0xdd, 0xb4, 0x0c, - 0x0d, 0xa0, 0x14, 0x2b, 0xf8, 0x9b, 0x02, 0x16, 0x5e, 0xee, 0xd8, 0xb6, 0xa5, 0x8c, 0xc6, 0x6a, - 0x9d, 0x15, 0xbf, 0x6e, 0xc1, 0x79, 0x06, 0xbc, 0xe9, 0xdd, 0xce, 0x4a, 0xe0, 0x71, 0x26, 0xc5, - 0x2a, 0x90, 0x8b, 0xa8, 0x5f, 0xc9, 0x44, 0x66, 0xc1, 0x43, 0x5c, 0xf4, 0x3e, 0x14, 0x7f, 0x8c, - 0x8d, 0x4f, 0x4b, 0xea, 0xdf, 0xcd, 0xa2, 0xa1, 0xaa, 0xed, 0x78, 0x3c, 0x2e, 0xc0, 0xd1, 0x5e, - 0x85, 0x84, 0x63, 0x4b, 0x7e, 0x0f, 0xc7, 0x96, 0xf3, 0x28, 0x2f, 0xd8, 0x21, 0xb2, 0xcb, 0x4f, - 0xc3, 0x70, 0x34, 0x80, 0xaa, 0xdf, 0x99, 0x45, 0xe8, 0xcd, 0xe7, 0x9e, 0x3b, 0xc6, 0x02, 0x52, - 0x7f, 0x24, 0x83, 0xc6, 0xf8, 0x6d, 0xbc, 0x10, 0x59, 0xa7, 0xdf, 0x7f, 0x47, 0x11, 0xc7, 0x25, - 0x03, 0x69, 0x3e, 0x8e, 0x2e, 0x01, 0x33, 0x0f, 0x4d, 0x0f, 0x2e, 0x24, 0x85, 0xd0, 0x3a, 0x84, - 0xc3, 0xc4, 0x25, 0xc0, 0xa7, 0xc3, 0xcf, 0xfa, 0xef, 0x0c, 0xb9, 0x70, 0xdd, 0xa3, 0x05, 0x66, - 0x12, 0xdf, 0x1a, 0xd4, 0x5f, 0xca, 0xa3, 0xfc, 0xcc, 0x43, 0xd2, 0x38, 0xe2, 0x5d, 0x23, 0xdc, - 0x5e, 0xe4, 0xf7, 0x79, 0x7b, 0xf1, 0x28, 0x0f, 0xa7, 0xaf, 0x86, 0xfd, 0xd9, 0x27, 0x57, 0x1f, - 0xe9, 0xf9, 0x68, 0xf5, 0x7e, 0x4f, 0x1f, 0xbd, 0x77, 0xf7, 0x7f, 0x96, 0x43, 0xb9, 0xda, 0x74, - 0xf5, 0x44, 0x6f, 0x0e, 0x55, 0x6f, 0xba, 0x3f, 0x4c, 0xa9, 0xc1, 0x5d, 0xf3, 0x40, 0x68, 0x0a, - 0x16, 0xb9, 0x56, 0xfe, 0x56, 0x0e, 0x8d, 0xd6, 0x66, 0x97, 0xab, 0xc2, 0x75, 0xcf, 0x6d, 0x66, - 0xae, 0x03, 0x86, 0x23, 0xac, 0x4b, 0xcf, 0xc7, 0xf6, 0x33, 0x77, 0x2a, 0x96, 0xf7, 0xfc, 0xcd, - 0xbb, 0x7a, 0xb3, 0x43, 0xe0, 0x6e, 0x80, 0x19, 0xf7, 0xb9, 0xe6, 0xbb, 0xe4, 0x47, 0xc1, 0xbb, - 0xd7, 0x67, 0x80, 0x3f, 0x81, 0x72, 0x77, 0xf8, 0xb3, 0x6b, 0x1a, 0x9f, 0x1b, 0xd7, 0x19, 0x1f, - 0x3a, 0x09, 0xe6, 0x3a, 0xa6, 0x01, 0x1c, 0x68, 0x29, 0x5a, 0xf8, 0x16, 0x5f, 0x80, 0x77, 0x55, - 0x78, 0xd5, 0x2f, 0x7c, 0xab, 0x52, 0xc6, 0x35, 0x34, 0x54, 0x25, 0x4e, 0xcb, 0x84, 0x8e, 0xf2, - 0xe7, 0xec, 0xee, 0x4c, 0xe8, 0x49, 0x65, 0xa8, 0x1d, 0x16, 0x02, 0x66, 0x22, 0x17, 0xfc, 0x16, - 0x42, 0x6c, 0x8f, 0xb2, 0xcb, 0x68, 0x6d, 0x17, 0x60, 0xdf, 0xcf, 0xb6, 0x96, 0x09, 0x7b, 0x3c, - 0x81, 0x19, 0x5e, 0x47, 0x85, 0x05, 0xdb, 0x30, 0xef, 0x9b, 0xcc, 0xbe, 0x0a, 0x2a, 0xe8, 0xdb, - 0xd9, 0xaa, 0x81, 0x6e, 0x25, 0x5b, 0x42, 0xb9, 0xa4, 0x6a, 0x62, 0x8c, 0xd5, 0x7f, 0xd2, 0x8b, - 0xf2, 0xb4, 0xdb, 0x4f, 0xc6, 0xef, 0x7e, 0xc6, 0x6f, 0x09, 0x15, 0xee, 0xd9, 0xce, 0xba, 0x69, - 0xad, 0x06, 0xa6, 0xaf, 0xfc, 0x6c, 0x0a, 0xcf, 0xf5, 0x1b, 0x0c, 0x57, 0x0f, 0xac, 0x64, 0xb5, - 0x18, 0xf9, 0x0e, 0x23, 0xf8, 0x45, 0x84, 0x98, 0x43, 0x2b, 0xd0, 0x0c, 0x84, 0x1e, 0xe9, 0xcc, - 0xdd, 0x15, 0xac, 0x69, 0x45, 0x8f, 0xf4, 0x90, 0x98, 0x1e, 0xc2, 0xd9, 0x83, 0xe7, 0x20, 0x18, - 0xd7, 0xc2, 0x21, 0x1c, 0x1e, 0x3c, 0xc5, 0x4d, 0x00, 0x7b, 0xfa, 0xac, 0x22, 0x24, 0x5c, 0x22, - 0xa3, 0x88, 0x20, 0xa4, 0xc9, 0x81, 0xc7, 0x80, 0x4a, 0xb8, 0x43, 0xd6, 0x04, 0x1e, 0xf8, 0xf9, - 0xc8, 0x2b, 0x17, 0x96, 0xb8, 0xa5, 0x3e, 0x72, 0x85, 0x56, 0x12, 0xc3, 0x3b, 0x59, 0x49, 0xa8, - 0x5f, 0xc8, 0xa2, 0xc1, 0x5a, 0x67, 0xc5, 0xdd, 0x74, 0x3d, 0xd2, 0x3a, 0xe2, 0x6a, 0xec, 0x1f, - 0xaf, 0xf2, 0x89, 0xc7, 0xab, 0xa7, 0x7c, 0xa1, 0x08, 0xf7, 0x8e, 0xc1, 0x96, 0xce, 0x17, 0xc7, - 0x2f, 0x66, 0x51, 0x81, 0xdd, 0x8e, 0x97, 0x4d, 0xb7, 0x71, 0x00, 0x16, 0xbb, 0x87, 0x2f, 0x95, - 0xfd, 0xbd, 0x28, 0xed, 0xc2, 0x0e, 0x5a, 0xfd, 0x7c, 0x16, 0x0d, 0x95, 0x3a, 0xde, 0x5a, 0xc9, - 0x03, 0xdd, 0x3a, 0x96, 0xe7, 0x93, 0xdf, 0xce, 0xa0, 0x31, 0xda, 0x90, 0x65, 0x7b, 0x9d, 0x58, - 0x07, 0x70, 0xf1, 0x28, 0x5e, 0x20, 0x66, 0x1f, 0xf1, 0x02, 0xd1, 0x97, 0x65, 0x6e, 0x6f, 0xb2, - 0x84, 0xeb, 0x72, 0xcd, 0x6e, 0x92, 0xa3, 0xfd, 0x19, 0x07, 0x78, 0x5d, 0xee, 0x0b, 0xe4, 0x00, - 0xae, 0x52, 0xbe, 0x3d, 0x04, 0xf2, 0xc3, 0x59, 0x74, 0x9a, 0x87, 0x09, 0xe5, 0x87, 0xa3, 0x13, - 0x5d, 0x49, 0x15, 0xcd, 0x89, 0xd6, 0x70, 0xd1, 0xfc, 0x54, 0x0e, 0x9d, 0x86, 0x60, 0x6a, 0x74, - 0xcf, 0xf8, 0x6d, 0x30, 0x51, 0xe2, 0x86, 0xfc, 0x42, 0xb3, 0x90, 0xf0, 0x42, 0xf3, 0x67, 0x5b, - 0xc5, 0xe7, 0x57, 0x4d, 0x6f, 0xad, 0xb3, 0x32, 0xd9, 0xb0, 0x5b, 0xd7, 0x56, 0x1d, 0xfd, 0x81, - 0xc9, 0xde, 0x26, 0xf4, 0xe6, 0xb5, 0x20, 0xe2, 0xb6, 0xde, 0x36, 0x79, 0x2c, 0xee, 0x1a, 0x6c, - 0xc4, 0x28, 0x57, 0xff, 0x6d, 0xc7, 0x45, 0xe8, 0x75, 0xdb, 0xb4, 0xb8, 0x55, 0x03, 0x5b, 0x85, - 0x6b, 0x74, 0xf3, 0xfa, 0x8e, 0x6d, 0x5a, 0xf5, 0xa8, 0x69, 0xc3, 0x5e, 0xeb, 0x0b, 0x59, 0x6b, - 0x42, 0x35, 0xea, 0xbf, 0xc9, 0xa0, 0xc7, 0x64, 0x2d, 0xfe, 0x76, 0x58, 0xd8, 0xfe, 0x56, 0x16, - 0x9d, 0xb9, 0x05, 0xc2, 0x09, 0x5e, 0x99, 0x4f, 0xe6, 0x2d, 0x3e, 0x38, 0x13, 0x64, 0x73, 0x32, - 0x71, 0x71, 0xd9, 0xfc, 0xab, 0x0c, 0x3a, 0xb5, 0x54, 0x29, 0x4f, 0x7f, 0x9b, 0x68, 0x4d, 0xfc, - 0x7b, 0x8e, 0x76, 0x4f, 0xc3, 0xf7, 0xd4, 0x4a, 0x0b, 0xf3, 0xdf, 0x4e, 0xfd, 0x23, 0x7d, 0xcf, - 0x11, 0xef, 0x9f, 0xdf, 0xea, 0x43, 0x43, 0xb7, 0x3b, 0x2b, 0x84, 0xbf, 0xf9, 0x1d, 0xeb, 0x03, - 0xf5, 0x75, 0x34, 0xc4, 0xc5, 0x00, 0x97, 0x51, 0x42, 0xe0, 0x11, 0xee, 0x48, 0xca, 0x7c, 0xbb, - 0x45, 0x22, 0x7c, 0x1e, 0xe5, 0xef, 0x12, 0x67, 0x45, 0xb4, 0xc9, 0x7f, 0x40, 0x9c, 0x15, 0x0d, - 0xa0, 0x78, 0x3e, 0x34, 0x95, 0x2b, 0x55, 0x2b, 0x10, 0x84, 0x9a, 0xdf, 0x83, 0x41, 0x54, 0xed, - 0xc0, 0x9c, 0x40, 0x6f, 0x9b, 0x2c, 0x7c, 0xb5, 0xe8, 0x0f, 0x14, 0x2d, 0x89, 0x17, 0xd1, 0xb8, - 0xf8, 0x9e, 0xcc, 0x22, 0x30, 0x0f, 0x24, 0xb0, 0x4b, 0x8a, 0xbd, 0x1c, 0x2f, 0x8a, 0x5f, 0x45, - 0xc3, 0x3e, 0x10, 0x5e, 0xc6, 0x07, 0xc3, 0xb0, 0x9f, 0x01, 0xab, 0x48, 0x78, 0x77, 0xa9, 0x80, - 0xc8, 0x00, 0x6e, 0x77, 0x50, 0x02, 0x83, 0x88, 0xa5, 0x81, 0x54, 0x00, 0x7f, 0x0c, 0x18, 0xb4, - 0x6d, 0xcb, 0x25, 0xf0, 0x06, 0x38, 0x04, 0x06, 0xeb, 0x60, 0x8a, 0xe7, 0x70, 0x38, 0x73, 0x4b, - 0x90, 0xc8, 0xf0, 0x12, 0x42, 0xe1, 0x5b, 0x0d, 0x77, 0xfe, 0xda, 0xf3, 0x2b, 0x92, 0xc0, 0x42, - 0xbc, 0x65, 0x1d, 0x79, 0x94, 0x5b, 0x56, 0xf5, 0x77, 0xb3, 0x68, 0xa8, 0xd4, 0x6e, 0x07, 0x43, - 0xe1, 0x59, 0xd4, 0x57, 0x6a, 0xb7, 0xef, 0x68, 0x15, 0x31, 0x0c, 0xa4, 0xde, 0x6e, 0xd7, 0x3b, - 0x8e, 0x29, 0x9a, 0xda, 0x30, 0x22, 0x3c, 0x8d, 0x46, 0x4a, 0xed, 0x76, 0xb5, 0xb3, 0xd2, 0x34, - 0x1b, 0x42, 0x54, 0x79, 0x96, 0x00, 0xa3, 0xdd, 0xae, 0xb7, 0x01, 0x13, 0x4d, 0x2d, 0x20, 0x97, - 0xc1, 0x6f, 0x83, 0xcb, 0x34, 0x0f, 0x6a, 0xce, 0xc2, 0x26, 0xab, 0x41, 0x00, 0xc8, 0xb0, 0x6d, - 0x93, 0x01, 0x11, 0x0b, 0x94, 0x79, 0xde, 0x0f, 0x37, 0x4a, 0x2b, 0x8a, 0x05, 0x2f, 0x0f, 0x59, - 0xe2, 0x8f, 0xa2, 0xfe, 0x52, 0xbb, 0x2d, 0x5c, 0xe3, 0xc1, 0x5b, 0x2d, 0x2d, 0x15, 0xe9, 0x63, - 0x9f, 0x6c, 0xe2, 0x65, 0x34, 0x2a, 0x57, 0xb6, 0xa7, 0x40, 0x9b, 0x7f, 0x9a, 0x81, 0x0f, 0x3a, - 0xe2, 0xa6, 0x62, 0x37, 0x50, 0xae, 0xd4, 0x6e, 0xf3, 0xf9, 0xe8, 0x54, 0x42, 0x7f, 0x44, 0xdd, - 0x47, 0x4a, 0xed, 0xb6, 0xff, 0xe9, 0xcc, 0x54, 0xf5, 0x78, 0x7d, 0xfa, 0xd7, 0xd8, 0xa7, 0x1f, - 0x6d, 0x7b, 0x50, 0xf5, 0x97, 0x72, 0x68, 0xac, 0xd4, 0x6e, 0x9f, 0x04, 0xe8, 0x3c, 0x28, 0x27, - 0x95, 0xe7, 0x10, 0x12, 0xa6, 0xc7, 0xfe, 0xc0, 0x64, 0x7b, 0x48, 0x98, 0x1a, 0x95, 0x8c, 0x26, - 0x10, 0xf9, 0xea, 0x37, 0xb0, 0x27, 0xf5, 0xfb, 0x7c, 0x0e, 0xa6, 0xe2, 0xa3, 0xee, 0x70, 0xff, - 0x41, 0xe9, 0x36, 0xde, 0x07, 0x7d, 0x7b, 0xea, 0x83, 0xdf, 0x94, 0x06, 0x0f, 0x04, 0x7c, 0x3c, - 0xe9, 0x85, 0xde, 0x7d, 0x6d, 0x8b, 0x47, 0x45, 0x61, 0x72, 0x2f, 0x60, 0x3f, 0x08, 0x3d, 0xf7, - 0x49, 0x6f, 0x50, 0x54, 0xdd, 0x34, 0xb4, 0x08, 0xad, 0xdf, 0x87, 0xfd, 0x7b, 0xea, 0xc3, 0xad, - 0x2c, 0xf8, 0x9d, 0x04, 0x3e, 0xed, 0xfb, 0x3f, 0x5d, 0x5c, 0x43, 0x88, 0x3d, 0xe8, 0x04, 0xd6, - 0x62, 0x23, 0xcc, 0x7d, 0x95, 0xc5, 0xa6, 0xe7, 0xee, 0xab, 0x21, 0x49, 0xf0, 0xf0, 0x9c, 0x4b, - 0x7c, 0x78, 0xbe, 0x82, 0x06, 0x34, 0x7d, 0xe3, 0x8d, 0x0e, 0x71, 0x36, 0xf9, 0x76, 0x86, 0x85, - 0x8c, 0xd1, 0x37, 0xea, 0x9f, 0xa3, 0x40, 0x2d, 0x40, 0x63, 0x35, 0x70, 0x5c, 0x12, 0x1e, 0xda, - 0xd8, 0xed, 0x5e, 0xe0, 0xae, 0xf4, 0x28, 0x8a, 0x8e, 0x5f, 0x42, 0xb9, 0xd2, 0xbd, 0x1a, 0x97, - 0x6c, 0xd0, 0xb5, 0xa5, 0x7b, 0x35, 0x2e, 0xaf, 0xd4, 0xb2, 0xf7, 0x6a, 0xea, 0xe7, 0xb3, 0x08, - 0xc7, 0x29, 0xf1, 0xf3, 0x68, 0x10, 0xa0, 0xab, 0x54, 0x67, 0xc4, 0xa4, 0x46, 0x1b, 0x6e, 0xdd, - 0x01, 0xa8, 0xb4, 0xb9, 0xf3, 0x49, 0xf1, 0x8b, 0x90, 0xbf, 0x8d, 0xa7, 0xd5, 0x90, 0x92, 0x1a, - 0x6d, 0xb8, 0x7e, 0xc6, 0xb3, 0x48, 0xfa, 0x36, 0x4e, 0x0c, 0xfb, 0xc2, 0x7b, 0xb5, 0x39, 0xdb, - 0xf5, 0xb8, 0xa8, 0xd9, 0xbe, 0x70, 0xc3, 0x85, 0x6c, 0x5a, 0xd2, 0xbe, 0x90, 0x91, 0x41, 0x46, - 0x80, 0x7b, 0x35, 0x66, 0xfd, 0x6b, 0x68, 0x76, 0xd3, 0xdf, 0x50, 0xb2, 0x8c, 0x00, 0x1b, 0x6e, - 0x9d, 0x59, 0x0e, 0x1b, 0x90, 0x38, 0x4e, 0xca, 0x08, 0x20, 0x95, 0x52, 0xbf, 0x38, 0x80, 0x0a, - 0x65, 0xdd, 0xd3, 0x57, 0x74, 0x97, 0x08, 0xa7, 0xe9, 0x31, 0x1f, 0xe6, 0x7f, 0x8e, 0x20, 0x07, - 0x63, 0x25, 0xe1, 0x6b, 0xa2, 0x05, 0xf0, 0x27, 0x42, 0xbe, 0x41, 0xbe, 0x26, 0x31, 0x01, 0xc4, - 0x4a, 0xbd, 0xcd, 0xc1, 0x5a, 0x8c, 0x10, 0x5f, 0x45, 0x43, 0x3e, 0x8c, 0x1e, 0x00, 0x72, 0xa1, - 0xce, 0x18, 0x2b, 0x74, 0xff, 0xaf, 0x89, 0x68, 0xfc, 0x22, 0x1a, 0xf6, 0x7f, 0x0a, 0x5b, 0x6b, - 0x96, 0xcd, 0x62, 0x25, 0x76, 0x7a, 0x12, 0x49, 0xc5, 0xa2, 0x30, 0xbf, 0xf5, 0x4a, 0x45, 0x23, - 0x09, 0x23, 0x24, 0x52, 0xfc, 0x39, 0x34, 0xea, 0xff, 0xe6, 0x07, 0x06, 0x96, 0x5b, 0xe3, 0x6a, - 0x90, 0x97, 0x2e, 0x22, 0xd6, 0x49, 0x99, 0x9c, 0x1d, 0x1d, 0x1e, 0xf7, 0x73, 0x20, 0x18, 0x2b, - 0xf1, 0x93, 0x43, 0xa4, 0x02, 0x5c, 0x41, 0xe3, 0x3e, 0x24, 0xd4, 0xd0, 0xfe, 0xf0, 0xc4, 0x68, - 0xac, 0xd4, 0x13, 0x95, 0x34, 0x5e, 0x0a, 0x37, 0xd1, 0x79, 0x09, 0x68, 0xb8, 0x6b, 0xe6, 0x7d, - 0x8f, 0x1f, 0xf7, 0x78, 0xfc, 0x36, 0x9e, 0xf4, 0x26, 0xe0, 0xca, 0x68, 0xfc, 0xec, 0x55, 0x72, - 0x64, 0xfd, 0xae, 0xdc, 0x70, 0x0d, 0x9d, 0xf6, 0xf1, 0xb7, 0xa6, 0xab, 0x55, 0xc7, 0x7e, 0x87, - 0x34, 0xbc, 0x4a, 0x99, 0x1f, 0x97, 0x21, 0xae, 0x87, 0xb1, 0x52, 0x5f, 0x6d, 0xb4, 0xa9, 0x52, - 0x50, 0x9c, 0xcc, 0x3c, 0xb1, 0x30, 0xbe, 0x8b, 0xce, 0x08, 0xf0, 0x8a, 0xe5, 0x7a, 0xba, 0xd5, - 0x20, 0x95, 0x32, 0x3f, 0x43, 0xc3, 0x79, 0x9e, 0x73, 0x35, 0x39, 0x52, 0x66, 0x9b, 0x5c, 0x1c, - 0xbf, 0x8c, 0x46, 0x7c, 0x04, 0x7b, 0xff, 0x18, 0x82, 0xf7, 0x0f, 0x18, 0x92, 0xc6, 0x4a, 0x3d, - 0xea, 0xa4, 0x22, 0x13, 0x8b, 0x1a, 0x05, 0x69, 0x41, 0x87, 0x25, 0x8d, 0xf2, 0x36, 0xdb, 0x89, - 0xca, 0x08, 0xa9, 0x42, 0x5f, 0x0d, 0x35, 0x6a, 0xc9, 0x31, 0x57, 0x4d, 0x76, 0x92, 0xf6, 0xfd, - 0x52, 0x56, 0xea, 0x36, 0x00, 0x93, 0xf4, 0x83, 0x91, 0x4f, 0x94, 0xd0, 0xa9, 0x04, 0x1d, 0xdb, - 0xd3, 0x89, 0xf1, 0x0b, 0xd9, 0xb0, 0x11, 0x47, 0xfc, 0xd8, 0x38, 0x85, 0x06, 0xfc, 0x2f, 0xe1, - 0x9b, 0x07, 0x25, 0x6d, 0x68, 0x46, 0x79, 0xf8, 0x78, 0x49, 0x1c, 0x47, 0xfc, 0x28, 0x79, 0x10, - 0xe2, 0x78, 0x2f, 0x13, 0x8a, 0xe3, 0x88, 0x1f, 0x2f, 0xff, 0x7a, 0x3e, 0x9c, 0x93, 0x4e, 0xce, - 0x98, 0x07, 0xb5, 0x4d, 0x0e, 0xcd, 0x8b, 0xfa, 0xf6, 0xe0, 0x1f, 0x22, 0xaa, 0x66, 0xff, 0xa3, - 0xa9, 0x26, 0x7e, 0x19, 0x0d, 0x55, 0x6d, 0xd7, 0x5b, 0x75, 0x88, 0x5b, 0x0d, 0xe2, 0x8f, 0x82, - 0x6f, 0x51, 0x9b, 0x83, 0xeb, 0x6d, 0x69, 0xf6, 0x17, 0xc9, 0xd5, 0xdf, 0xcf, 0xc5, 0xb4, 0x81, - 0x6d, 0x5c, 0x8f, 0xa4, 0x36, 0x1c, 0xc0, 0x50, 0xc7, 0xd7, 0xc3, 0x55, 0x90, 0xed, 0xf0, 0x7b, - 0x85, 0xe0, 0x2a, 0x2b, 0x7c, 0x83, 0x2f, 0x93, 0xe0, 0x4f, 0xa3, 0x73, 0x12, 0xa0, 0xaa, 0x3b, - 0x7a, 0x8b, 0x78, 0x61, 0xae, 0x17, 0x70, 0x97, 0xf7, 0x4b, 0xd7, 0xdb, 0x01, 0x5a, 0xcc, 0x1f, - 0x93, 0xc2, 0x41, 0x50, 0xad, 0xfe, 0x3d, 0x58, 0xae, 0xfd, 0xe7, 0x2c, 0x1a, 0x09, 0x3a, 0x5a, - 0x77, 0x5c, 0x72, 0x7c, 0x7b, 0xf4, 0xe3, 0x68, 0x04, 0xbc, 0x37, 0x5b, 0xc4, 0xf2, 0x84, 0xa4, - 0x8a, 0x2c, 0xe0, 0xa3, 0x8f, 0xe0, 0xb1, 0x7d, 0x25, 0x42, 0x5c, 0x44, 0xbd, 0x4c, 0x07, 0x04, - 0x9f, 0x5a, 0xa6, 0x00, 0x0c, 0xae, 0xfe, 0x78, 0x0e, 0x0d, 0xfb, 0x52, 0x9e, 0x32, 0x8f, 0xea, - 0x8d, 0xcf, 0xe1, 0x0a, 0xf9, 0x1a, 0x42, 0x55, 0xdb, 0xf1, 0xf4, 0xa6, 0x90, 0x9a, 0x1d, 0x8e, - 0x4a, 0x6d, 0x80, 0xb2, 0x32, 0x02, 0x09, 0x9e, 0x44, 0x48, 0x18, 0x60, 0xfd, 0x30, 0xc0, 0x46, - 0xb7, 0xb7, 0x8a, 0x28, 0x1c, 0x57, 0x9a, 0x40, 0xa1, 0xfe, 0x5a, 0x16, 0x8d, 0xf9, 0x9d, 0x34, - 0xf3, 0x90, 0x34, 0x3a, 0xde, 0x31, 0x1e, 0x0c, 0xb2, 0xb4, 0x7b, 0x77, 0x94, 0xb6, 0xfa, 0x3f, - 0x84, 0x89, 0x64, 0xba, 0x69, 0x9f, 0x4c, 0x24, 0x7f, 0x11, 0x3a, 0xae, 0x7e, 0x77, 0x0e, 0x9d, - 0xf6, 0xa5, 0x3e, 0xdb, 0xb1, 0x60, 0x93, 0x31, 0xad, 0x37, 0x9b, 0xc7, 0x79, 0x5d, 0x1e, 0xf2, - 0x05, 0xb1, 0xc4, 0xc3, 0x21, 0xf0, 0x38, 0xeb, 0xf7, 0x39, 0xb8, 0x6e, 0x9b, 0x86, 0x26, 0x12, - 0xe1, 0x57, 0xd1, 0xb0, 0xff, 0xb3, 0xe4, 0xac, 0xfa, 0x8b, 0x31, 0x5c, 0x19, 0x04, 0x85, 0x74, - 0x47, 0xf2, 0xfa, 0x90, 0x0a, 0xa8, 0xff, 0xa5, 0x0f, 0x4d, 0xdc, 0x33, 0x2d, 0xc3, 0xde, 0x70, - 0xfd, 0x30, 0xfd, 0x47, 0x7e, 0xcb, 0x7c, 0xd8, 0xe1, 0xf9, 0xdf, 0x40, 0x67, 0xa2, 0x22, 0x75, - 0x82, 0xe0, 0x49, 0xbc, 0x77, 0x36, 0x18, 0x41, 0xdd, 0x0f, 0xd8, 0xcf, 0xef, 0xdd, 0xb4, 0xe4, - 0x92, 0xd1, 0x88, 0xff, 0xfd, 0xbb, 0x89, 0xf8, 0xff, 0x0c, 0xea, 0x2b, 0xdb, 0x2d, 0xdd, 0xf4, - 0xfd, 0xff, 0x60, 0x14, 0x07, 0xf5, 0x02, 0x46, 0xe3, 0x14, 0x94, 0x3f, 0xaf, 0x18, 0xba, 0x6c, - 0x30, 0xe4, 0xef, 0x17, 0xe8, 0xb8, 0xc4, 0xd1, 0x44, 0x22, 0x6c, 0xa3, 0x11, 0x5e, 0x1d, 0xbf, - 0x25, 0x43, 0x70, 0x4b, 0x16, 0xe4, 0x55, 0x4c, 0x57, 0xab, 0x49, 0xa9, 0x1c, 0xbb, 0x2e, 0x63, - 0x89, 0x08, 0xf8, 0xc7, 0xb0, 0xfb, 0x32, 0x4d, 0xe6, 0x2f, 0x08, 0x01, 0x26, 0x99, 0xa1, 0xb8, - 0x10, 0x60, 0x96, 0x11, 0x89, 0xf0, 0x0c, 0x1a, 0x2f, 0x35, 0x9b, 0xf6, 0x46, 0x10, 0xa5, 0x88, - 0xaa, 0xc4, 0x30, 0x44, 0x6a, 0x85, 0xcb, 0x17, 0x9d, 0x22, 0xe1, 0xe3, 0xea, 0x0d, 0x8e, 0xd6, - 0xe2, 0x25, 0x26, 0x5e, 0x43, 0x38, 0xde, 0xe6, 0x3d, 0x5d, 0xbf, 0x7c, 0x31, 0x8b, 0x70, 0xe4, - 0x1c, 0x32, 0x73, 0x8c, 0xb7, 0x53, 0xea, 0xcf, 0x65, 0xd0, 0x78, 0x2c, 0x7a, 0x18, 0xbe, 0x81, - 0x10, 0x83, 0x08, 0x51, 0x2b, 0xc0, 0x0d, 0x2c, 0x8c, 0x28, 0xc6, 0x97, 0x92, 0x90, 0x0c, 0x5f, - 0x43, 0x03, 0xec, 0x57, 0x90, 0x26, 0x34, 0x5a, 0xa4, 0xd3, 0x31, 0x0d, 0x2d, 0x20, 0x0a, 0x6b, - 0x81, 0x7b, 0xbc, 0x5c, 0x62, 0x11, 0x6f, 0xb3, 0x1d, 0xd4, 0x42, 0xc9, 0x68, 0x07, 0x0e, 0x07, - 0x0d, 0x2e, 0x19, 0x87, 0xd5, 0x75, 0x7d, 0x3c, 0x10, 0x5b, 0x6e, 0xa7, 0x40, 0x6c, 0x91, 0xb9, - 0x89, 0x47, 0x5e, 0x3b, 0x38, 0xe3, 0xd2, 0x2f, 0x67, 0xd1, 0x58, 0x50, 0xeb, 0x21, 0x5e, 0x19, - 0x7d, 0x80, 0x44, 0xf2, 0xa5, 0x0c, 0x52, 0xa6, 0xcc, 0x66, 0xd3, 0xb4, 0x56, 0x2b, 0xd6, 0x7d, - 0xdb, 0x69, 0xc1, 0xe4, 0x71, 0x78, 0xb7, 0x8b, 0xea, 0xf7, 0x65, 0xd0, 0x38, 0x6f, 0xd0, 0xb4, - 0xee, 0x18, 0x87, 0x77, 0xed, 0x1b, 0x6d, 0xc9, 0xe1, 0xe9, 0x8b, 0xfa, 0xd5, 0x2c, 0x42, 0xf3, - 0x76, 0x63, 0xfd, 0x88, 0x5b, 0xd0, 0x7f, 0x62, 0xe7, 0xec, 0xb8, 0x05, 0x39, 0x3b, 0xae, 0x92, - 0xf1, 0xf3, 0xe3, 0xd2, 0x4a, 0x29, 0x1d, 0xdf, 0xd5, 0x04, 0x95, 0x8a, 0xe9, 0x77, 0x59, 0xa5, - 0xdb, 0x5b, 0xc5, 0x7c, 0xd3, 0x6e, 0xac, 0x6b, 0x40, 0xaf, 0xfe, 0x79, 0x86, 0xc9, 0xee, 0x88, - 0x5b, 0xd8, 0xfb, 0x9f, 0x9f, 0xdf, 0xe3, 0xe7, 0xff, 0xb5, 0x0c, 0x3a, 0xad, 0x91, 0x86, 0xfd, - 0x80, 0x38, 0x9b, 0xd3, 0xb6, 0x41, 0x6e, 0x11, 0x8b, 0x38, 0x87, 0x35, 0xa2, 0xfe, 0x31, 0x84, - 0x9a, 0x0c, 0x1b, 0x73, 0xc7, 0x25, 0xc6, 0xd1, 0x09, 0x78, 0xaa, 0xfe, 0xa3, 0x7e, 0xa4, 0x24, - 0xee, 0x10, 0x8f, 0xec, 0xae, 0x28, 0x75, 0xdb, 0x9f, 0x3f, 0xa8, 0x6d, 0x7f, 0xef, 0xde, 0xb6, - 0xfd, 0x7d, 0x7b, 0xdd, 0xf6, 0xf7, 0xef, 0x66, 0xdb, 0xdf, 0x8a, 0x6e, 0xfb, 0x07, 0x60, 0xdb, - 0x7f, 0xa3, 0xeb, 0xb6, 0x7f, 0xc6, 0x32, 0x1e, 0x71, 0xd3, 0x7f, 0x64, 0xd3, 0x7c, 0x3c, 0xca, - 0x69, 0xe5, 0x32, 0x9d, 0x14, 0x1b, 0xb6, 0x63, 0x10, 0x83, 0x1f, 0x52, 0xe0, 0x56, 0xde, 0xe1, - 0x30, 0x2d, 0xc0, 0xc6, 0x72, 0xa6, 0x8c, 0xec, 0x26, 0x67, 0xca, 0x01, 0x1c, 0x63, 0xbe, 0x90, - 0x45, 0xe3, 0xd3, 0xc4, 0xf1, 0x58, 0x3c, 0x91, 0x83, 0x78, 0x48, 0x2e, 0xa1, 0x31, 0x81, 0x21, - 0xec, 0xc8, 0x85, 0x5c, 0xff, 0x0d, 0xe2, 0x78, 0xd1, 0xb7, 0xf5, 0x28, 0x3d, 0xad, 0xde, 0x8f, - 0x5b, 0xcc, 0xc7, 0x6e, 0x50, 0xbd, 0x0f, 0x67, 0x82, 0x34, 0xf9, 0x2f, 0x2d, 0xa0, 0x17, 0x42, - 0x11, 0xe7, 0xf7, 0x1e, 0x8a, 0x58, 0xfd, 0x99, 0x0c, 0xba, 0xa4, 0x11, 0x8b, 0x6c, 0xe8, 0x2b, - 0x4d, 0x22, 0x34, 0x8b, 0xaf, 0x0c, 0x74, 0xd6, 0x30, 0xdd, 0x96, 0xee, 0x35, 0xd6, 0xf6, 0x25, - 0xa3, 0x59, 0x34, 0x2c, 0xce, 0x5f, 0x7b, 0x98, 0xdb, 0xa4, 0x72, 0xea, 0xaf, 0xe6, 0x50, 0xff, - 0x94, 0xed, 0xed, 0x3b, 0x63, 0x78, 0x38, 0xe5, 0x67, 0xf7, 0x70, 0x2f, 0xf2, 0x51, 0xa8, 0x5c, - 0x88, 0x24, 0x08, 0x86, 0x17, 0x2b, 0x76, 0x2c, 0xe2, 0xa2, 0x4f, 0xb6, 0xc7, 0xa8, 0xd8, 0xcf, - 0xa3, 0x41, 0xf0, 0xf6, 0x14, 0x6e, 0x2e, 0xc1, 0xac, 0xc9, 0xa3, 0xc0, 0x68, 0x1d, 0x21, 0x29, - 0xfe, 0xb4, 0x14, 0x00, 0xa5, 0x6f, 0xff, 0x51, 0xb4, 0xc5, 0x58, 0x28, 0x07, 0x16, 0xac, 0x5a, - 0xfd, 0x56, 0x1e, 0x0d, 0xfb, 0xc6, 0x2c, 0x87, 0xd4, 0x83, 0xcf, 0xa2, 0xbe, 0x39, 0x5b, 0x88, - 0x8a, 0x08, 0xc6, 0x2f, 0x6b, 0xb6, 0x1b, 0xb1, 0xea, 0xe1, 0x44, 0xf8, 0x06, 0x1a, 0x58, 0xb4, - 0x0d, 0xd1, 0x74, 0x0b, 0xc6, 0xb4, 0x65, 0x1b, 0x31, 0xd7, 0x97, 0x80, 0x10, 0x5f, 0x42, 0x79, - 0xb0, 0x7a, 0x13, 0xae, 0x9e, 0x23, 0x96, 0x6e, 0x80, 0x17, 0x74, 0xa3, 0x6f, 0xaf, 0xba, 0xd1, - 0xff, 0xa8, 0xba, 0x31, 0x70, 0xb0, 0xba, 0xf1, 0x16, 0x1a, 0x86, 0x9a, 0xfc, 0xa8, 0xdf, 0x3b, - 0x2f, 0x6f, 0x8f, 0xf1, 0x15, 0x68, 0x84, 0xb5, 0x9b, 0xc7, 0xfe, 0x86, 0x85, 0x47, 0x62, 0x15, - 0x51, 0x3b, 0xb4, 0x0f, 0xb5, 0xfb, 0xfd, 0x0c, 0xea, 0xbf, 0x63, 0xad, 0x5b, 0xf6, 0xc6, 0xfe, - 0x34, 0xee, 0x06, 0x1a, 0xe2, 0x6c, 0x84, 0x39, 0x1e, 0xbc, 0x99, 0x3a, 0x0c, 0x5c, 0x07, 0x4e, - 0x9a, 0x48, 0x85, 0x5f, 0x0e, 0x0a, 0x81, 0x61, 0x6b, 0x2e, 0x8c, 0x2b, 0xea, 0x17, 0x6a, 0xc8, - 0xa1, 0x10, 0x45, 0x72, 0x7c, 0x9e, 0xe7, 0xc0, 0x17, 0x02, 0xeb, 0xd0, 0xa6, 0xb0, 0x14, 0xf8, - 0xea, 0xbf, 0xcc, 0xa2, 0xd1, 0xc8, 0xf5, 0xd3, 0x33, 0x68, 0x90, 0x5f, 0xff, 0x98, 0x7e, 0x6c, - 0x46, 0x30, 0x7c, 0x0d, 0x80, 0xda, 0x00, 0xfb, 0xb3, 0x62, 0xe0, 0x4f, 0xa2, 0x7e, 0xdb, 0x85, - 0xa5, 0x09, 0xbe, 0x65, 0x34, 0x1c, 0x42, 0x4b, 0x35, 0xda, 0x76, 0x36, 0x38, 0x38, 0x89, 0xa8, - 0x91, 0xb6, 0x0b, 0x9f, 0x76, 0x13, 0x0d, 0xea, 0xae, 0x4b, 0xbc, 0xba, 0xa7, 0xaf, 0x8a, 0xe1, - 0x1a, 0x03, 0xa0, 0x38, 0x3a, 0x00, 0xb8, 0xac, 0xaf, 0xe2, 0xd7, 0xd0, 0x48, 0xc3, 0x21, 0xb0, - 0x78, 0xe9, 0x4d, 0xda, 0x4a, 0x61, 0x73, 0x29, 0x21, 0xc4, 0x1b, 0xff, 0x10, 0x51, 0x31, 0xf0, - 0x5d, 0x34, 0xc2, 0x3f, 0x87, 0x59, 0x9d, 0xc1, 0x40, 0x1b, 0x0d, 0x17, 0x13, 0x26, 0x12, 0x66, - 0x77, 0xc6, 0x8d, 0x0f, 0x45, 0x72, 0x91, 0xaf, 0x21, 0x90, 0xaa, 0x5f, 0xcf, 0xd0, 0x0d, 0x0f, - 0x05, 0x04, 0xe9, 0x34, 0x5b, 0x7b, 0xd4, 0x95, 0x56, 0x18, 0x31, 0xbf, 0xcf, 0xed, 0x32, 0x3b, - 0x69, 0x1c, 0x8b, 0x27, 0x51, 0x9f, 0x21, 0xde, 0xfd, 0x9c, 0x95, 0x3f, 0xc2, 0xaf, 0x47, 0xe3, - 0x54, 0xf8, 0x32, 0xca, 0xd3, 0x0d, 0x6d, 0xf4, 0xe0, 0x27, 0xae, 0x91, 0x1a, 0x50, 0xa8, 0xdf, - 0x99, 0x45, 0xc3, 0xc2, 0xd7, 0x5c, 0xdf, 0xd7, 0xe7, 0xbc, 0xb4, 0xbb, 0x66, 0x72, 0x3b, 0x58, - 0x80, 0x05, 0x4d, 0xbe, 0x19, 0x88, 0x62, 0x57, 0x4f, 0x10, 0x5c, 0x30, 0xcf, 0xf3, 0x0f, 0xed, - 0xdb, 0xfd, 0x21, 0x88, 0xd2, 0xbf, 0x9e, 0x1f, 0xc8, 0x16, 0x72, 0xaf, 0xe7, 0x07, 0xf2, 0x85, - 0x5e, 0xf0, 0xac, 0x87, 0x68, 0x52, 0xec, 0x84, 0x69, 0xdd, 0x37, 0x57, 0x8f, 0xb8, 0xdd, 0xe0, - 0xc1, 0x46, 0x1d, 0x88, 0xc8, 0xe6, 0x88, 0x1b, 0x11, 0xbe, 0xaf, 0xb2, 0x39, 0x49, 0x60, 0xc0, - 0x65, 0xf3, 0xef, 0x32, 0x48, 0x49, 0x94, 0x4d, 0xe9, 0x90, 0x5e, 0xbe, 0x0f, 0x2e, 0x8d, 0xc1, - 0x37, 0xb3, 0x68, 0xbc, 0x62, 0x79, 0x64, 0x95, 0x9d, 0x7b, 0x8e, 0xf8, 0x54, 0x71, 0x9b, 0xa5, - 0x31, 0xe5, 0x1f, 0xc3, 0xfb, 0xfc, 0xf1, 0xe0, 0x54, 0x19, 0xa2, 0x52, 0x38, 0x89, 0xa5, 0x0f, - 0x30, 0xbd, 0x51, 0x44, 0xc8, 0x47, 0x7c, 0xce, 0x39, 0x1a, 0x42, 0x3e, 0xe2, 0x93, 0xd7, 0x07, - 0x54, 0xc8, 0xff, 0x3d, 0x83, 0x4e, 0x25, 0x54, 0x0e, 0xc9, 0x01, 0x3b, 0x2b, 0x10, 0x72, 0x21, - 0x23, 0x24, 0x07, 0xec, 0xac, 0x40, 0xb4, 0x05, 0xcd, 0x47, 0xe2, 0x65, 0x70, 0xac, 0x5a, 0xaa, - 0x94, 0xa7, 0xb9, 0x54, 0x55, 0xc1, 0x45, 0x8c, 0x82, 0x93, 0xbe, 0x2c, 0x70, 0xbe, 0xb2, 0x4d, - 0xa3, 0x11, 0x71, 0xbe, 0xa2, 0x65, 0xf0, 0x67, 0xd0, 0x60, 0xe9, 0xdd, 0x8e, 0x43, 0x80, 0x2f, - 0x93, 0xf8, 0x87, 0x02, 0xbe, 0x3e, 0x22, 0x89, 0x33, 0xf3, 0x23, 0xa3, 0x14, 0x51, 0xde, 0x21, - 0x43, 0xf5, 0x8b, 0x19, 0x34, 0x91, 0xde, 0x3a, 0xfc, 0x51, 0xd4, 0x4f, 0x4f, 0xb6, 0x25, 0x6d, - 0x91, 0x7f, 0x3a, 0x4b, 0xf9, 0x61, 0x37, 0x49, 0x5d, 0x77, 0xc4, 0x8d, 0xb7, 0x4f, 0x86, 0x5f, - 0x41, 0x43, 0x15, 0xd7, 0xed, 0x10, 0xa7, 0x76, 0xe3, 0x8e, 0x56, 0xe1, 0x67, 0x2a, 0xd8, 0xb3, - 0x9b, 0x00, 0xae, 0xbb, 0x37, 0x22, 0x41, 0x15, 0x44, 0x7a, 0xf5, 0xfb, 0x33, 0xe8, 0x7c, 0xb7, - 0xaf, 0xa2, 0x07, 0xf8, 0x65, 0x62, 0xe9, 0x96, 0xc7, 0x53, 0xeb, 0xf2, 0x23, 0x8a, 0x07, 0x30, - 0xf9, 0x90, 0x11, 0x10, 0xd2, 0x42, 0xec, 0x76, 0x2c, 0x78, 0x8e, 0x67, 0x37, 0x79, 0x00, 0x8b, - 0x14, 0xf2, 0x09, 0xd5, 0x9f, 0x7e, 0x13, 0xf5, 0x2e, 0x59, 0x64, 0xe9, 0x3e, 0x7e, 0x4e, 0x48, - 0xe0, 0xc6, 0x07, 0xda, 0xb8, 0x38, 0x60, 0x00, 0x31, 0xd7, 0xa3, 0x09, 0x69, 0xde, 0x6e, 0x8a, - 0x59, 0xa8, 0xb8, 0x3a, 0x60, 0xb1, 0x0c, 0xc3, 0xcc, 0xf5, 0x68, 0x62, 0xb6, 0xaa, 0x9b, 0x62, - 0x72, 0x25, 0xde, 0xd9, 0x52, 0x29, 0x86, 0xf1, 0x4b, 0xf1, 0x69, 0x60, 0x3e, 0x29, 0x03, 0x51, - 0x74, 0x4f, 0x10, 0xa7, 0x98, 0xeb, 0xd1, 0x92, 0x33, 0x17, 0x0d, 0x8b, 0x86, 0x31, 0xd1, 0x07, - 0x39, 0x11, 0x37, 0xd7, 0xa3, 0x49, 0xb4, 0xf8, 0x85, 0x20, 0xcd, 0xe3, 0xeb, 0xb6, 0x69, 0x45, - 0xbd, 0x2b, 0x05, 0xd4, 0x5c, 0x8f, 0x26, 0x52, 0x0a, 0x95, 0x56, 0x1d, 0x33, 0xc8, 0xc1, 0x16, - 0xad, 0x14, 0x70, 0x42, 0xa5, 0xf0, 0x1b, 0xbf, 0x82, 0x46, 0x02, 0xb7, 0xd5, 0x77, 0x48, 0xc3, - 0xe3, 0x57, 0x22, 0x67, 0x22, 0x85, 0x19, 0x72, 0xae, 0x47, 0x93, 0xa9, 0xf1, 0x65, 0x3f, 0xc1, - 0x3f, 0xbf, 0xeb, 0x18, 0x15, 0xa6, 0x33, 0xf3, 0x5d, 0x2a, 0x25, 0x8e, 0xa7, 0xbd, 0x13, 0xbe, - 0x1d, 0xf0, 0x0b, 0x0c, 0x1c, 0xa9, 0x65, 0xc6, 0x32, 0x68, 0xef, 0x08, 0x0f, 0x47, 0xaf, 0x45, - 0x53, 0x20, 0xf3, 0xc4, 0xda, 0x67, 0x23, 0x25, 0x39, 0x76, 0xae, 0x47, 0x8b, 0xa6, 0x4c, 0x7e, - 0x41, 0x4a, 0xbf, 0xcb, 0xe3, 0xa7, 0x44, 0xa5, 0x4a, 0x51, 0x82, 0x54, 0x21, 0x51, 0xef, 0x6b, - 0xd1, 0x7c, 0xb0, 0x3c, 0x5a, 0xca, 0xd9, 0xe4, 0xac, 0xa1, 0x42, 0xd5, 0x7e, 0xfe, 0xd8, 0x17, - 0xa4, 0xbc, 0x9d, 0x90, 0x1a, 0x3b, 0xa1, 0x6a, 0xdd, 0xd3, 0xc5, 0xaa, 0xd9, 0xf9, 0x52, 0xca, - 0x20, 0x09, 0x09, 0x6e, 0xe2, 0x1d, 0x0a, 0x38, 0xa1, 0x43, 0x59, 0xb6, 0xc9, 0x17, 0xa4, 0x24, - 0x26, 0x3c, 0x83, 0x4d, 0x50, 0xa9, 0x80, 0xa2, 0x95, 0x8a, 0xe9, 0x4e, 0x6e, 0x8a, 0xb9, 0x3d, - 0x94, 0x71, 0xb9, 0x83, 0x42, 0x0c, 0xed, 0x20, 0x21, 0x07, 0x48, 0x11, 0xf2, 0x06, 0x28, 0x18, - 0xc8, 0x87, 0x82, 0x16, 0x4e, 0x57, 0xe7, 0x7a, 0x34, 0xc8, 0x28, 0xa0, 0xb2, 0x8c, 0x14, 0xca, - 0x29, 0xa0, 0x18, 0x0e, 0xf2, 0xa3, 0x3e, 0x24, 0x8d, 0xb9, 0x1e, 0x8d, 0x65, 0xab, 0x78, 0x4e, - 0x88, 0xfd, 0xac, 0x9c, 0x96, 0xa7, 0x88, 0x00, 0x41, 0xa7, 0x88, 0x30, 0x42, 0xf4, 0x6c, 0x3c, - 0x3e, 0xb2, 0x72, 0x46, 0x5e, 0x51, 0xa3, 0xf8, 0xb9, 0x1e, 0x2d, 0x1e, 0x53, 0xf9, 0x05, 0x29, - 0x64, 0xb0, 0x72, 0x36, 0xe2, 0xd2, 0x1c, 0xa2, 0xa8, 0xb8, 0xc4, 0xe0, 0xc2, 0x4b, 0x89, 0x49, - 0xbe, 0x94, 0x73, 0xf2, 0x72, 0x9c, 0x40, 0x32, 0xd7, 0xa3, 0x25, 0xa6, 0x07, 0x9b, 0x8e, 0x05, - 0xee, 0x55, 0x14, 0xf9, 0xdd, 0x32, 0x82, 0x9e, 0xeb, 0xd1, 0x62, 0xa1, 0x7e, 0x6f, 0x8a, 0x11, - 0x73, 0x95, 0xc7, 0xe4, 0x4e, 0x0c, 0x31, 0xb4, 0x13, 0x85, 0xc8, 0xba, 0x37, 0xc5, 0xb0, 0xb2, - 0xca, 0x44, 0xbc, 0x54, 0x38, 0x73, 0x0a, 0xe1, 0x67, 0xb5, 0xe4, 0xd8, 0xab, 0xca, 0xe3, 0x3c, - 0x34, 0x3f, 0x2f, 0x9f, 0x44, 0x33, 0xd7, 0xa3, 0x25, 0xc7, 0x6d, 0xd5, 0x92, 0x83, 0x96, 0x2a, - 0xe7, 0xbb, 0xf1, 0x0c, 0x5a, 0x97, 0x1c, 0xf0, 0x54, 0xef, 0x12, 0x42, 0x52, 0xb9, 0x20, 0xc7, - 0x53, 0x4a, 0x25, 0x9c, 0xeb, 0xd1, 0xba, 0x04, 0xa2, 0xbc, 0x93, 0x12, 0xcf, 0x51, 0xb9, 0x28, - 0x67, 0xe6, 0x48, 0x24, 0x9a, 0xeb, 0xd1, 0x52, 0xa2, 0x41, 0xde, 0x49, 0x09, 0x85, 0xa8, 0x14, - 0xbb, 0xb2, 0x0d, 0xe4, 0x91, 0x12, 0x48, 0x71, 0x29, 0x31, 0x8a, 0xa0, 0xf2, 0x84, 0xac, 0xba, - 0x09, 0x24, 0x54, 0x75, 0x93, 0xe2, 0x0f, 0x2e, 0x25, 0x86, 0xf1, 0x53, 0x9e, 0xec, 0xc2, 0x30, - 0x68, 0x63, 0x62, 0x00, 0xc0, 0xa5, 0xc4, 0x38, 0x7a, 0x8a, 0x2a, 0x33, 0x4c, 0x20, 0xa1, 0x0c, - 0x93, 0x22, 0xf0, 0x2d, 0x25, 0x06, 0xb2, 0x53, 0x9e, 0xea, 0xc2, 0x30, 0x6c, 0x61, 0x52, 0x08, - 0xbc, 0x17, 0xa4, 0x48, 0x72, 0xca, 0x87, 0xe4, 0x79, 0x43, 0x40, 0xd1, 0x79, 0x43, 0x8c, 0x39, - 0x37, 0x1d, 0x8b, 0x95, 0xa3, 0x7c, 0x58, 0x1e, 0xe6, 0x11, 0x34, 0x1d, 0xe6, 0xd1, 0xe8, 0x3a, - 0xd3, 0xb1, 0x98, 0x21, 0xca, 0xa5, 0x34, 0x26, 0x80, 0x96, 0x99, 0xb0, 0x28, 0x23, 0x95, 0x84, - 0xa0, 0x15, 0xca, 0xd3, 0xb2, 0xcd, 0x5d, 0x8c, 0x60, 0xae, 0x47, 0x4b, 0x08, 0x75, 0xa1, 0x25, - 0x7b, 0x68, 0x2a, 0x97, 0xe5, 0x61, 0x9b, 0x44, 0x43, 0x87, 0x6d, 0xa2, 0x77, 0xe7, 0x7c, 0x92, - 0x7d, 0xad, 0x72, 0x45, 0xde, 0x98, 0xc5, 0x29, 0xe8, 0xc6, 0x2c, 0xc1, 0x2e, 0x57, 0x4b, 0xf6, - 0x1a, 0x54, 0x9e, 0xe9, 0xda, 0x42, 0xa0, 0x49, 0x68, 0x21, 0x73, 0xa2, 0x0b, 0xf7, 0x4e, 0x77, - 0xda, 0x4d, 0x5b, 0x37, 0x94, 0x8f, 0x24, 0xee, 0x9d, 0x18, 0x52, 0xd8, 0x3b, 0x31, 0x00, 0x5d, - 0xe5, 0x45, 0xfb, 0x53, 0xe5, 0xaa, 0xbc, 0xca, 0x8b, 0x38, 0xba, 0xca, 0x4b, 0xb6, 0xaa, 0xd3, - 0x31, 0x5b, 0x4d, 0xe5, 0x59, 0x59, 0x01, 0x22, 0x68, 0xaa, 0x00, 0x51, 0xeb, 0xce, 0xb7, 0xd3, - 0xad, 0x1b, 0x95, 0x49, 0xe0, 0xf6, 0x44, 0x90, 0x01, 0x3e, 0x85, 0x6e, 0xae, 0x47, 0x4b, 0xb7, - 0x90, 0xac, 0x24, 0x18, 0x2b, 0x2a, 0xd7, 0x64, 0x05, 0x8b, 0x11, 0x50, 0x05, 0x8b, 0x9b, 0x38, - 0x56, 0x12, 0xac, 0x0d, 0x95, 0x8f, 0xa6, 0xb2, 0x0a, 0xbe, 0x39, 0xc1, 0x46, 0xf1, 0xa6, 0x68, - 0x2e, 0xa8, 0x3c, 0x27, 0x2f, 0x76, 0x21, 0x86, 0x2e, 0x76, 0x82, 0x59, 0xe1, 0x4d, 0xd1, 0x50, - 0x4e, 0xb9, 0x1e, 0x2f, 0x15, 0x2e, 0x91, 0x82, 0x41, 0x9d, 0x96, 0x6c, 0x5f, 0xa6, 0xdc, 0x90, - 0xb5, 0x2e, 0x89, 0x86, 0x6a, 0x5d, 0xa2, 0x6d, 0xda, 0x6c, 0xdc, 0x4c, 0x4c, 0xb9, 0x19, 0xbd, - 0x4b, 0x90, 0xf1, 0x74, 0xe7, 0x13, 0x33, 0x2d, 0x7b, 0x2d, 0x1a, 0x3e, 0x40, 0xf9, 0x58, 0xe4, - 0x31, 0x43, 0xc2, 0xd2, 0xfd, 0x6d, 0x24, 0xdc, 0xc0, 0x6b, 0x51, 0x8f, 0x7b, 0xe5, 0xf9, 0x64, - 0x0e, 0x81, 0xae, 0x44, 0x3d, 0xf4, 0x5f, 0x8b, 0x3a, 0xa9, 0x2b, 0x2f, 0x24, 0x73, 0x08, 0xa4, - 0x1b, 0x75, 0x6a, 0x7f, 0x4e, 0x08, 0x9b, 0xa7, 0x7c, 0x5c, 0xde, 0x3a, 0x06, 0x08, 0xba, 0x75, - 0x0c, 0x83, 0xeb, 0x3d, 0x27, 0x84, 0x9b, 0x53, 0x5e, 0x8c, 0x15, 0x09, 0x1a, 0x2b, 0x04, 0xa5, - 0x7b, 0x4e, 0x08, 0xd3, 0xa6, 0xbc, 0x14, 0x2b, 0x12, 0xb4, 0x4e, 0x08, 0xe6, 0x66, 0x74, 0xf3, - 0xc3, 0x51, 0x3e, 0x21, 0x5f, 0x71, 0xa4, 0x53, 0xce, 0xf5, 0x68, 0xdd, 0xfc, 0x79, 0xde, 0x4e, - 0x37, 0xba, 0x53, 0x5e, 0x96, 0x87, 0x70, 0x1a, 0x1d, 0x1d, 0xc2, 0xa9, 0x86, 0x7b, 0xaf, 0x44, - 0x7c, 0x72, 0x95, 0x57, 0xe4, 0x29, 0x4e, 0x42, 0xd2, 0x29, 0x2e, 0xea, 0xc1, 0x2b, 0x39, 0x9b, - 0x2a, 0x9f, 0x94, 0xa7, 0x38, 0x11, 0x47, 0xa7, 0x38, 0xc9, 0x31, 0x75, 0x3a, 0xe6, 0x03, 0xa9, - 0xbc, 0x2a, 0x4f, 0x71, 0x11, 0x34, 0x9d, 0xe2, 0xa2, 0x5e, 0x93, 0xaf, 0x44, 0x5c, 0x01, 0x95, - 0xd7, 0x92, 0xdb, 0x0f, 0x48, 0xb1, 0xfd, 0xcc, 0x71, 0x50, 0x4b, 0xf6, 0x69, 0x53, 0x4a, 0xf2, - 0xf8, 0x4d, 0xa2, 0xa1, 0xe3, 0x37, 0xd1, 0x1f, 0x6e, 0x29, 0x31, 0x2f, 0xa6, 0x32, 0xd5, 0xe5, - 0xe0, 0x10, 0x6e, 0x45, 0x92, 0x32, 0x6a, 0x8a, 0x67, 0x64, 0x76, 0x10, 0x9a, 0x4e, 0x39, 0x23, - 0xfb, 0xc7, 0xa0, 0x08, 0x3d, 0x9d, 0x5d, 0x63, 0x36, 0x60, 0x4a, 0x59, 0x9e, 0x5d, 0x63, 0x04, - 0x74, 0x76, 0x8d, 0x5b, 0x8e, 0xcd, 0xa2, 0x02, 0xd7, 0x22, 0x66, 0xda, 0x66, 0x5a, 0xab, 0xca, - 0x4c, 0xc4, 0xa5, 0x24, 0x82, 0xa7, 0xb3, 0x53, 0x14, 0x06, 0xeb, 0x35, 0x83, 0x4d, 0x37, 0xcd, - 0xf6, 0x8a, 0xad, 0x3b, 0x46, 0x8d, 0x58, 0x86, 0x32, 0x1b, 0x59, 0xaf, 0x13, 0x68, 0x60, 0xbd, - 0x4e, 0x80, 0x83, 0xd3, 0x7b, 0x04, 0xae, 0x91, 0x06, 0x31, 0x1f, 0x10, 0xe5, 0x16, 0xb0, 0x2d, - 0xa6, 0xb1, 0xe5, 0x64, 0x73, 0x3d, 0x5a, 0x1a, 0x07, 0xba, 0x57, 0x5f, 0xd8, 0xac, 0xbd, 0x31, - 0x1f, 0xb8, 0x51, 0x56, 0x1d, 0xd2, 0xd6, 0x1d, 0xa2, 0xcc, 0xc9, 0x7b, 0xf5, 0x44, 0x22, 0xba, - 0x57, 0x4f, 0x44, 0xc4, 0xd9, 0xfa, 0x63, 0xa1, 0xd2, 0x8d, 0x6d, 0x38, 0x22, 0x92, 0x4b, 0xd3, - 0xd9, 0x49, 0x46, 0x50, 0x01, 0xcd, 0xdb, 0xd6, 0x2a, 0xdc, 0x54, 0xbc, 0x2e, 0xcf, 0x4e, 0xe9, - 0x94, 0x74, 0x76, 0x4a, 0xc7, 0x52, 0x55, 0x97, 0xb1, 0x6c, 0x0c, 0xde, 0x96, 0x55, 0x3d, 0x81, - 0x84, 0xaa, 0x7a, 0x02, 0x38, 0xce, 0x50, 0x23, 0x2e, 0xf1, 0x94, 0xf9, 0x6e, 0x0c, 0x81, 0x24, - 0xce, 0x10, 0xc0, 0x71, 0x86, 0xb3, 0xc4, 0x6b, 0xac, 0x29, 0x0b, 0xdd, 0x18, 0x02, 0x49, 0x9c, - 0x21, 0x80, 0xe9, 0x61, 0x53, 0x06, 0x4f, 0x75, 0x9a, 0xeb, 0x7e, 0x9f, 0x2d, 0xca, 0x87, 0xcd, - 0x54, 0x42, 0x7a, 0xd8, 0x4c, 0x45, 0xe2, 0xef, 0xdf, 0xb5, 0x8d, 0xa2, 0xb2, 0x04, 0x15, 0x4e, - 0x86, 0xfb, 0x82, 0xdd, 0x94, 0x9a, 0xeb, 0xd1, 0x76, 0x6b, 0x03, 0xf9, 0x91, 0xc0, 0x94, 0x48, - 0xa9, 0x42, 0x55, 0x63, 0xc1, 0x5d, 0x05, 0x03, 0xcf, 0xf5, 0x68, 0x81, 0xb1, 0xd1, 0x0b, 0x68, - 0x08, 0x3e, 0xaa, 0x62, 0x99, 0x5e, 0x79, 0x4a, 0x79, 0x43, 0x3e, 0x32, 0x09, 0x28, 0x7a, 0x64, - 0x12, 0x7e, 0xd2, 0x49, 0x1c, 0x7e, 0xb2, 0x29, 0xa6, 0x3c, 0xa5, 0x68, 0xf2, 0x24, 0x2e, 0x21, - 0xe9, 0x24, 0x2e, 0x01, 0x82, 0x7a, 0xcb, 0x8e, 0xdd, 0x2e, 0x4f, 0x29, 0xb5, 0x84, 0x7a, 0x19, - 0x2a, 0xa8, 0x97, 0xfd, 0x0c, 0xea, 0xad, 0xad, 0x75, 0xbc, 0x32, 0xfd, 0xc6, 0xe5, 0x84, 0x7a, - 0x7d, 0x64, 0x50, 0xaf, 0x0f, 0xa0, 0x53, 0x21, 0x00, 0xaa, 0x8e, 0x4d, 0x27, 0xed, 0xdb, 0x66, - 0xb3, 0xa9, 0xdc, 0x91, 0xa7, 0xc2, 0x28, 0x9e, 0x4e, 0x85, 0x51, 0x18, 0xdd, 0x7a, 0xb2, 0x56, - 0x91, 0x95, 0xce, 0xaa, 0x72, 0x57, 0xde, 0x7a, 0x86, 0x18, 0xba, 0xf5, 0x0c, 0x7f, 0xc1, 0xe9, - 0x82, 0xfe, 0xd2, 0xc8, 0x7d, 0x87, 0xb8, 0x6b, 0xca, 0xbd, 0xc8, 0xe9, 0x42, 0xc0, 0xc1, 0xe9, - 0x42, 0xf8, 0x8d, 0x57, 0xd1, 0xe3, 0xd2, 0x42, 0xe3, 0xbf, 0x3d, 0xd5, 0x88, 0xee, 0x34, 0xd6, - 0x94, 0x37, 0x81, 0xd5, 0x53, 0x89, 0x4b, 0x95, 0x4c, 0x3a, 0xd7, 0xa3, 0x75, 0xe3, 0x04, 0xc7, - 0xf2, 0x37, 0xe6, 0x59, 0x6c, 0x1b, 0xad, 0x3a, 0xed, 0x1f, 0x42, 0xdf, 0x8a, 0x1c, 0xcb, 0xe3, - 0x24, 0x70, 0x2c, 0x8f, 0x83, 0x71, 0x1b, 0x5d, 0x8c, 0x1c, 0xd5, 0x16, 0xf4, 0x26, 0x3d, 0x97, - 0x10, 0xa3, 0xaa, 0x37, 0xd6, 0x89, 0xa7, 0x7c, 0x0a, 0x78, 0x5f, 0x4a, 0x39, 0xf0, 0x45, 0xa8, - 0xe7, 0x7a, 0xb4, 0x1d, 0xf8, 0x61, 0x95, 0x65, 0x5e, 0x54, 0x3e, 0x2d, 0xdf, 0x6f, 0x52, 0xd8, - 0x5c, 0x8f, 0xc6, 0xb2, 0x32, 0xbe, 0x8d, 0x94, 0x3b, 0xed, 0x55, 0x47, 0x37, 0x08, 0xdb, 0x68, - 0xc1, 0xde, 0x8d, 0x6f, 0x40, 0x3f, 0x23, 0xef, 0xd2, 0xd2, 0xe8, 0xe8, 0x2e, 0x2d, 0x0d, 0x47, - 0x15, 0x55, 0x0a, 0xe3, 0xaa, 0x7c, 0x56, 0x56, 0x54, 0x09, 0x49, 0x15, 0x55, 0x0e, 0xfa, 0xfa, - 0x26, 0x3a, 0x1b, 0x9c, 0xe7, 0xf9, 0xfa, 0xcb, 0x3a, 0x4d, 0x79, 0x1b, 0xf8, 0x5c, 0x8c, 0x3d, - 0x06, 0x48, 0x54, 0x73, 0x3d, 0x5a, 0x4a, 0x79, 0xba, 0xe2, 0xc6, 0x22, 0x94, 0xf3, 0xed, 0xc5, - 0x77, 0xc8, 0x2b, 0x6e, 0x0a, 0x19, 0x5d, 0x71, 0x53, 0x50, 0x89, 0xcc, 0xb9, 0x50, 0xf5, 0x1d, - 0x98, 0x07, 0x32, 0x4d, 0xe3, 0x90, 0xc8, 0x9c, 0xef, 0xd4, 0x56, 0x76, 0x60, 0x1e, 0xec, 0xd6, - 0xd2, 0x38, 0xe0, 0xcb, 0xa8, 0xaf, 0x56, 0x5b, 0xd0, 0x3a, 0x96, 0xd2, 0x88, 0xd8, 0x80, 0x01, - 0x74, 0xae, 0x47, 0xe3, 0x78, 0xba, 0x0d, 0x9a, 0x69, 0xea, 0xae, 0x67, 0x36, 0x5c, 0x18, 0x31, - 0xfe, 0x08, 0x31, 0xe4, 0x6d, 0x50, 0x12, 0x0d, 0xdd, 0x06, 0x25, 0xc1, 0xe9, 0x7e, 0x71, 0x5a, - 0x77, 0x5d, 0xdd, 0x32, 0x1c, 0x7d, 0x0a, 0x96, 0x09, 0x12, 0xb1, 0x94, 0x97, 0xb0, 0x74, 0xbf, - 0x28, 0x43, 0xe0, 0xf2, 0xdd, 0x87, 0xf8, 0xdb, 0x9c, 0xfb, 0x91, 0xcb, 0xf7, 0x08, 0x1e, 0x2e, - 0xdf, 0x23, 0x30, 0xd8, 0x77, 0xfa, 0x30, 0x8d, 0xac, 0x9a, 0x90, 0x27, 0x79, 0x35, 0xb2, 0xef, - 0x8c, 0x12, 0xc0, 0xbe, 0x33, 0x0a, 0x94, 0x9a, 0xe4, 0x2f, 0xb7, 0x6b, 0x29, 0x4d, 0x0a, 0x57, - 0xd9, 0x58, 0x19, 0xba, 0x7e, 0x87, 0x83, 0xa3, 0xbc, 0x69, 0xe9, 0x2d, 0xbb, 0x3c, 0xe5, 0x4b, - 0xdd, 0x94, 0xd7, 0xef, 0x54, 0x42, 0xba, 0x7e, 0xa7, 0x22, 0xe9, 0xec, 0xea, 0x1f, 0xb4, 0xd6, - 0x74, 0x87, 0x18, 0x41, 0xf6, 0x50, 0x76, 0x34, 0x7c, 0x47, 0x9e, 0x5d, 0xbb, 0x90, 0xd2, 0xd9, - 0xb5, 0x0b, 0x9a, 0x6e, 0xf2, 0x92, 0xd1, 0x1a, 0xd1, 0x0d, 0x65, 0x5d, 0xde, 0xe4, 0xa5, 0x53, - 0xd2, 0x4d, 0x5e, 0x3a, 0x36, 0xfd, 0x73, 0xee, 0x39, 0xa6, 0x47, 0x94, 0xe6, 0x6e, 0x3e, 0x07, - 0x48, 0xd3, 0x3f, 0x07, 0xd0, 0xf4, 0x40, 0x18, 0xed, 0x90, 0x96, 0x7c, 0x20, 0x8c, 0x77, 0x43, - 0xb4, 0x04, 0xdd, 0xb1, 0x70, 0x87, 0x09, 0xc5, 0x92, 0x77, 0x2c, 0x1c, 0x4c, 0x77, 0x2c, 0xa1, - 0x4b, 0x85, 0x64, 0xa0, 0xaf, 0xd8, 0xf2, 0x1a, 0x2a, 0xe2, 0xe8, 0x1a, 0x2a, 0x19, 0xf3, 0xbf, - 0x20, 0x59, 0xcf, 0x2a, 0x6d, 0x79, 0xd7, 0x21, 0xa0, 0xe8, 0xae, 0x43, 0xb4, 0xb3, 0x9d, 0x46, - 0x63, 0xf0, 0x0a, 0xae, 0x75, 0x82, 0x77, 0x9c, 0xcf, 0xc9, 0x9f, 0x19, 0x41, 0xd3, 0xcf, 0x8c, - 0x80, 0x24, 0x26, 0x7c, 0xda, 0x72, 0x52, 0x98, 0x84, 0xf7, 0x83, 0x11, 0x10, 0x9e, 0x47, 0xb8, - 0x56, 0x5a, 0x98, 0xaf, 0x18, 0x55, 0xf1, 0x89, 0xcc, 0x95, 0x6f, 0x60, 0xe3, 0x14, 0x73, 0x3d, - 0x5a, 0x42, 0x39, 0xfc, 0x0e, 0x3a, 0xcf, 0xa1, 0xdc, 0x1b, 0x0e, 0x52, 0xb0, 0x19, 0xc1, 0x82, - 0xe0, 0xc9, 0xd6, 0x19, 0xdd, 0x68, 0xe7, 0x7a, 0xb4, 0xae, 0xbc, 0xd2, 0xeb, 0xe2, 0xeb, 0x43, - 0x67, 0x37, 0x75, 0x05, 0x8b, 0x44, 0x57, 0x5e, 0xe9, 0x75, 0x71, 0xb9, 0x3f, 0xd8, 0x4d, 0x5d, - 0x41, 0x27, 0x74, 0xe5, 0x85, 0x5d, 0x54, 0xec, 0x86, 0x2f, 0x35, 0x9b, 0xca, 0x06, 0x54, 0xf7, - 0xf4, 0x6e, 0xaa, 0x2b, 0xc1, 0x86, 0x73, 0x27, 0x8e, 0x74, 0x96, 0x5e, 0x6a, 0x13, 0xab, 0x26, - 0x2d, 0x40, 0x0f, 0xe5, 0x59, 0x3a, 0x46, 0x40, 0x67, 0xe9, 0x18, 0x90, 0x0e, 0x28, 0xd1, 0x08, - 0x5b, 0xd9, 0x94, 0x07, 0x94, 0x88, 0xa3, 0x03, 0x4a, 0x32, 0xd8, 0x5e, 0x42, 0xa7, 0x96, 0xd6, - 0x3d, 0xdd, 0xdf, 0x41, 0xba, 0xbc, 0x2b, 0xdf, 0x8d, 0x3c, 0x32, 0xc5, 0x49, 0xe0, 0x91, 0x29, - 0x0e, 0xa6, 0x63, 0x84, 0x82, 0x6b, 0x9b, 0x56, 0x63, 0x56, 0x37, 0x9b, 0x1d, 0x87, 0x28, 0xff, - 0x9f, 0x3c, 0x46, 0x22, 0x68, 0x3a, 0x46, 0x22, 0x20, 0xba, 0x40, 0x53, 0x50, 0xc9, 0x75, 0xcd, - 0x55, 0x8b, 0x9f, 0x2b, 0x3b, 0x4d, 0x4f, 0xf9, 0xff, 0xe5, 0x05, 0x3a, 0x89, 0x86, 0x2e, 0xd0, - 0x49, 0x70, 0xb8, 0x75, 0x4a, 0x48, 0x4f, 0xa8, 0xfc, 0xa5, 0xc8, 0xad, 0x53, 0x02, 0x0d, 0xdc, - 0x3a, 0x25, 0xa5, 0x36, 0x9c, 0x45, 0x05, 0xb6, 0x27, 0x9b, 0x37, 0x83, 0xb7, 0xea, 0xbf, 0x2c, - 0xaf, 0x8f, 0x51, 0x3c, 0x5d, 0x1f, 0xa3, 0x30, 0x99, 0x0f, 0xef, 0x82, 0xbf, 0x92, 0xc6, 0x27, - 0x90, 0x7f, 0xac, 0x0c, 0xbe, 0x25, 0xf2, 0xe1, 0x23, 0xe5, 0x3b, 0x33, 0x69, 0x8c, 0x82, 0xe1, - 0x11, 0x2b, 0x24, 0x33, 0xd2, 0xc8, 0x03, 0x93, 0x6c, 0x28, 0x9f, 0x4f, 0x65, 0xc4, 0x08, 0x64, - 0x46, 0x0c, 0x86, 0xdf, 0x42, 0x67, 0x43, 0xd8, 0x02, 0x69, 0xad, 0x04, 0x33, 0xd3, 0x77, 0x65, - 0xe4, 0x6d, 0x70, 0x32, 0x19, 0xdd, 0x06, 0x27, 0x63, 0x92, 0x58, 0x73, 0xd1, 0xfd, 0xd5, 0x1d, - 0x58, 0x07, 0x12, 0x4c, 0x61, 0x90, 0xc4, 0x9a, 0x4b, 0xf3, 0xbb, 0x77, 0x60, 0x1d, 0xc8, 0x34, - 0x85, 0x01, 0xfe, 0x81, 0x0c, 0xba, 0x94, 0x8c, 0x2a, 0x35, 0x9b, 0xb3, 0xb6, 0x13, 0xe2, 0x94, - 0xef, 0xc9, 0xc8, 0x17, 0x0d, 0xbb, 0x2b, 0x36, 0xd7, 0xa3, 0xed, 0xb2, 0x02, 0xfc, 0x49, 0x34, - 0x52, 0xea, 0x18, 0xa6, 0x07, 0x0f, 0x6f, 0x74, 0xe3, 0xfc, 0xbd, 0x99, 0xc8, 0x11, 0x47, 0xc4, - 0xc2, 0x11, 0x47, 0x04, 0xe0, 0xd7, 0xd1, 0x78, 0x8d, 0x34, 0x3a, 0x8e, 0xe9, 0x6d, 0x6a, 0x90, - 0x7a, 0x92, 0xf2, 0xf8, 0xbe, 0x8c, 0x3c, 0x89, 0xc5, 0x28, 0xe8, 0x24, 0x16, 0x03, 0x62, 0x82, - 0x26, 0x66, 0x1e, 0x7a, 0xc4, 0xb1, 0xf4, 0x26, 0x54, 0x52, 0xf3, 0x6c, 0x47, 0x5f, 0x25, 0x33, - 0x96, 0xbe, 0xd2, 0x24, 0xca, 0x17, 0x33, 0xf2, 0xbe, 0x2a, 0x9d, 0x94, 0xee, 0xab, 0xd2, 0xb1, - 0x78, 0x0d, 0x3d, 0x9e, 0x84, 0x2d, 0x9b, 0x2e, 0xd4, 0xf3, 0xa5, 0x8c, 0xbc, 0xb1, 0xea, 0x42, - 0x4b, 0x37, 0x56, 0x5d, 0xd0, 0x10, 0x9e, 0x3b, 0xc9, 0x2f, 0x44, 0xf9, 0xd1, 0x8c, 0x7c, 0xc9, - 0x98, 0x48, 0x35, 0xd7, 0xa3, 0xa5, 0xb8, 0x95, 0xdc, 0x4d, 0xf1, 0xa9, 0x50, 0x7e, 0xac, 0x3b, - 0xdf, 0x40, 0xe9, 0x53, 0x5c, 0x32, 0xee, 0xa6, 0xf8, 0x23, 0x28, 0x3f, 0xde, 0x9d, 0x6f, 0x68, - 0x17, 0x91, 0xec, 0xce, 0x50, 0x4f, 0xb7, 0xe5, 0x57, 0x7e, 0x22, 0x23, 0x9f, 0xd3, 0xd3, 0x08, - 0xe9, 0x39, 0x3d, 0xd5, 0x21, 0xe0, 0xf5, 0x04, 0x8b, 0x7a, 0xe5, 0x27, 0x23, 0x5a, 0x18, 0xa3, - 0xa0, 0x5a, 0x18, 0x37, 0xc4, 0x7f, 0x3d, 0xc1, 0x70, 0x5c, 0xf9, 0x7b, 0xe9, 0xbc, 0x02, 0xa1, - 0x26, 0xd8, 0x9b, 0xbf, 0x9e, 0x60, 0x1f, 0xad, 0xfc, 0xfd, 0x74, 0x5e, 0xe1, 0xf3, 0x6a, 0xdc, - 0xac, 0x9a, 0x4e, 0x48, 0x1d, 0xcf, 0x66, 0x9c, 0x25, 0x6d, 0xfa, 0xd9, 0xe8, 0x84, 0x94, 0x48, - 0x06, 0x13, 0x52, 0x22, 0x26, 0x89, 0x35, 0xff, 0xee, 0x9f, 0xdb, 0x81, 0xb5, 0x30, 0x8d, 0x26, - 0x62, 0x92, 0x58, 0x73, 0x31, 0x7c, 0x65, 0x07, 0xd6, 0xc2, 0x34, 0x9a, 0x88, 0xc1, 0x9f, 0x41, - 0xe7, 0x42, 0xcc, 0x5d, 0xe2, 0xb8, 0x61, 0xd7, 0xff, 0x7c, 0x46, 0xbe, 0x4a, 0x48, 0xa1, 0x9b, - 0xeb, 0xd1, 0xd2, 0x58, 0x24, 0x72, 0xe7, 0x42, 0xf9, 0x85, 0x9d, 0xb8, 0x87, 0xb7, 0x20, 0x29, - 0xa8, 0x44, 0xee, 0x5c, 0x2e, 0xbf, 0xb8, 0x13, 0xf7, 0xf0, 0x1a, 0x24, 0x05, 0x35, 0xd5, 0x8f, - 0x7a, 0x61, 0x6f, 0xa7, 0xfe, 0x68, 0x06, 0x0d, 0xd7, 0x3c, 0x87, 0xe8, 0x2d, 0xee, 0x97, 0x3c, - 0x81, 0x06, 0x98, 0x91, 0x84, 0x6f, 0xa7, 0xac, 0x05, 0xbf, 0xf1, 0x25, 0x34, 0x3a, 0xaf, 0xbb, - 0x1e, 0x94, 0xac, 0x58, 0x06, 0x79, 0x08, 0x06, 0xc2, 0x39, 0x2d, 0x02, 0xc5, 0xf3, 0x8c, 0x8e, - 0x95, 0x83, 0x80, 0x10, 0xb9, 0x1d, 0xdd, 0x71, 0x07, 0xde, 0xdb, 0x2a, 0xf6, 0x80, 0xf7, 0x6d, - 0xa4, 0xac, 0xfa, 0xf5, 0x0c, 0x8a, 0x99, 0x6f, 0x3c, 0xba, 0xff, 0xc0, 0x12, 0x1a, 0x8b, 0x04, - 0x21, 0xe1, 0x56, 0xce, 0xbb, 0x8c, 0x51, 0x12, 0x2d, 0x8d, 0x9f, 0x0e, 0xac, 0x6b, 0xef, 0x68, - 0xf3, 0xdc, 0xd5, 0xba, 0x7f, 0x7b, 0xab, 0x98, 0xeb, 0x38, 0x4d, 0x4d, 0x40, 0x71, 0x57, 0xc0, - 0x7f, 0x58, 0x08, 0x23, 0x2c, 0xe0, 0x4b, 0xdc, 0x99, 0x21, 0x13, 0x3a, 0x68, 0x47, 0xd2, 0x69, - 0x30, 0xe7, 0x85, 0x4f, 0xa2, 0xe1, 0x4a, 0xab, 0x4d, 0x1c, 0xd7, 0xb6, 0x74, 0xcf, 0xf6, 0xd3, - 0xf6, 0x81, 0xf3, 0xae, 0x29, 0xc0, 0x45, 0x87, 0x52, 0x91, 0x1e, 0x5f, 0xf1, 0xb3, 0x54, 0xe7, - 0x20, 0xb6, 0xc5, 0xa9, 0x84, 0x2c, 0xd5, 0x7e, 0xae, 0xe9, 0x2b, 0xa8, 0xf7, 0x8e, 0xab, 0x83, - 0x1d, 0x76, 0x40, 0xda, 0xa1, 0x00, 0x91, 0x14, 0x28, 0xf0, 0x55, 0xd4, 0x07, 0xe7, 0x56, 0x57, - 0xe9, 0x05, 0x5a, 0x70, 0x1b, 0x6f, 0x02, 0x44, 0x74, 0xd2, 0x65, 0x34, 0xf8, 0x36, 0x2a, 0x84, - 0x97, 0x72, 0x90, 0x68, 0xd2, 0x8f, 0xb1, 0x09, 0xa9, 0x2d, 0xd6, 0x03, 0x1c, 0xcb, 0x50, 0x29, - 0xb2, 0x88, 0x15, 0xc4, 0x73, 0x68, 0x2c, 0x84, 0x51, 0x11, 0xf9, 0xb1, 0x7d, 0x21, 0xb5, 0x8b, - 0xc0, 0x8b, 0x8a, 0x53, 0x64, 0x15, 0x2d, 0x86, 0x2b, 0xa8, 0xdf, 0xf7, 0x19, 0x1f, 0xd8, 0x51, - 0x49, 0x4f, 0x71, 0x9f, 0xf1, 0x7e, 0xd1, 0x5b, 0xdc, 0x2f, 0x8f, 0x67, 0xd1, 0xa8, 0x66, 0x77, - 0x3c, 0xb2, 0x6c, 0xf3, 0x3b, 0x47, 0x1e, 0xfc, 0x11, 0xda, 0xe4, 0x50, 0x4c, 0xdd, 0xb3, 0xfd, - 0xcc, 0x20, 0x62, 0x86, 0x0a, 0xb9, 0x14, 0x5e, 0x44, 0xe3, 0xb1, 0xeb, 0x4b, 0x31, 0x5f, 0x87, - 0xf0, 0x79, 0x71, 0x66, 0xf1, 0xa2, 0xf8, 0x7b, 0x33, 0xa8, 0x6f, 0xd9, 0xd1, 0x4d, 0xcf, 0xe5, - 0x26, 0xdc, 0x67, 0x26, 0x37, 0x1c, 0xbd, 0x4d, 0xf5, 0x63, 0x12, 0x82, 0x97, 0xdc, 0xd5, 0x9b, - 0x1d, 0xe2, 0x4e, 0xdd, 0xa3, 0x5f, 0xf7, 0xef, 0xb7, 0x8a, 0x9f, 0xd8, 0x43, 0xf6, 0xf0, 0x6b, - 0x01, 0x27, 0x56, 0x03, 0x55, 0x01, 0x0f, 0xfe, 0x12, 0x55, 0x80, 0xe1, 0xf0, 0x22, 0x42, 0xfc, - 0x53, 0x4b, 0xed, 0x36, 0xb7, 0x07, 0x17, 0x8c, 0x5d, 0x7d, 0x0c, 0x53, 0xec, 0x40, 0x60, 0x7a, - 0x5b, 0x4c, 0x57, 0x2a, 0x70, 0xa0, 0x5a, 0xb0, 0xcc, 0x5b, 0xe4, 0x8b, 0x69, 0x24, 0x94, 0xb8, - 0xdf, 0xd8, 0x04, 0x21, 0x45, 0x8b, 0xe1, 0x15, 0x34, 0xc6, 0xf9, 0x06, 0xd1, 0x18, 0x47, 0xe5, - 0x59, 0x21, 0x82, 0x66, 0x4a, 0x1b, 0xb4, 0xd1, 0xe0, 0x60, 0xb1, 0x8e, 0x48, 0x09, 0x3c, 0x15, - 0x06, 0x8b, 0x87, 0xdc, 0xa8, 0xca, 0x18, 0x68, 0xec, 0xf9, 0xed, 0xad, 0xa2, 0xe2, 0x97, 0x67, - 0x29, 0x55, 0x93, 0x12, 0xa7, 0x40, 0x11, 0x91, 0x07, 0xd3, 0xfa, 0x42, 0x02, 0x8f, 0xa8, 0xce, - 0xcb, 0x45, 0xf0, 0x34, 0x1a, 0x09, 0xcc, 0xd1, 0xee, 0xdc, 0xa9, 0x94, 0xc1, 0xe0, 0x9c, 0x27, - 0x01, 0x8d, 0x04, 0x7a, 0x14, 0x99, 0x48, 0x65, 0x04, 0xcf, 0x14, 0x66, 0x81, 0x1e, 0xf1, 0x4c, - 0x69, 0x27, 0x78, 0xa6, 0x54, 0xf1, 0x2b, 0x68, 0xa8, 0x74, 0xaf, 0xc6, 0x3d, 0x6e, 0x5c, 0xe5, - 0x54, 0x18, 0x61, 0x17, 0x72, 0xe7, 0x70, 0xef, 0x1c, 0xb1, 0xe9, 0x22, 0x3d, 0x9e, 0x41, 0xa3, - 0xd2, 0x8b, 0x96, 0xab, 0x9c, 0x06, 0x0e, 0x2c, 0x7d, 0x29, 0x60, 0xea, 0x3c, 0x83, 0xae, 0x94, - 0x20, 0x48, 0x2e, 0x44, 0xb5, 0x86, 0x6e, 0xbf, 0x9b, 0x4d, 0x7b, 0x43, 0x23, 0xe0, 0xdc, 0x03, - 0xe6, 0xeb, 0x03, 0x4c, 0x6b, 0x0c, 0x8e, 0xaa, 0x3b, 0x0c, 0x27, 0xa5, 0x6f, 0x92, 0x8b, 0xe1, - 0x77, 0x10, 0x86, 0xf8, 0xa6, 0xc4, 0xf0, 0x2f, 0x38, 0x2a, 0x65, 0x57, 0x39, 0x0b, 0x41, 0x9c, - 0x70, 0xd4, 0xbb, 0xac, 0x52, 0x9e, 0xba, 0xc4, 0xa7, 0x8f, 0x8b, 0x3a, 0x2b, 0x55, 0x0f, 0xb2, - 0xd7, 0x9a, 0x86, 0xd8, 0xe2, 0x04, 0xae, 0x78, 0x03, 0x9d, 0xab, 0x3a, 0xe4, 0x81, 0x69, 0x77, - 0x5c, 0x7f, 0xf9, 0xf0, 0xe7, 0xad, 0x73, 0x3b, 0xce, 0x5b, 0x4f, 0xf2, 0x8a, 0xcf, 0xb4, 0x1d, - 0xf2, 0xa0, 0xee, 0x87, 0xee, 0x91, 0x62, 0x5e, 0xa4, 0x71, 0xa7, 0xe2, 0x02, 0xc7, 0x26, 0x0e, - 0x37, 0x89, 0xab, 0x28, 0xe1, 0x54, 0xcb, 0xfc, 0xb4, 0xcc, 0x00, 0x27, 0x8a, 0x2b, 0x52, 0x0c, - 0x6b, 0x08, 0xdf, 0x9a, 0xf6, 0x2f, 0xbb, 0x4a, 0x8d, 0x86, 0xdd, 0xb1, 0x3c, 0x57, 0x79, 0x0c, - 0x98, 0xa9, 0x54, 0x2c, 0xab, 0x8d, 0x20, 0x8c, 0x57, 0x5d, 0xe7, 0x78, 0x51, 0x2c, 0xf1, 0xd2, - 0x78, 0x1e, 0x15, 0xaa, 0x8e, 0xf9, 0x40, 0xf7, 0xc8, 0x6d, 0xb2, 0x59, 0xb5, 0x9b, 0x66, 0x63, - 0x13, 0xac, 0xe8, 0xf9, 0x54, 0xd9, 0x66, 0xb8, 0xfa, 0x3a, 0xd9, 0xac, 0xb7, 0x01, 0x2b, 0x2e, - 0x2b, 0xd1, 0x92, 0x62, 0x58, 0x9d, 0xc7, 0x77, 0x17, 0x56, 0x87, 0xa0, 0x02, 0xbf, 0x2a, 0x7b, - 0xe8, 0x11, 0x8b, 0x2e, 0xf5, 0x2e, 0xb7, 0x98, 0x57, 0x22, 0x57, 0x6b, 0x01, 0x9e, 0xa7, 0x72, - 0x62, 0xa3, 0x8c, 0x04, 0x60, 0xb1, 0x61, 0xd1, 0x22, 0xea, 0x97, 0x72, 0xe2, 0xd4, 0x89, 0xcf, - 0xa3, 0xbc, 0x10, 0xd5, 0x15, 0xa2, 0x71, 0x40, 0x04, 0xac, 0x3c, 0x0f, 0xf5, 0x33, 0xc8, 0xb7, - 0x1d, 0x81, 0xdb, 0x18, 0x84, 0xbc, 0xf7, 0x43, 0x6d, 0x99, 0x86, 0x16, 0x12, 0x40, 0xb8, 0xf1, - 0x30, 0xe3, 0x65, 0x4e, 0x08, 0x37, 0x1e, 0x66, 0xbc, 0x94, 0xf2, 0x5d, 0x5e, 0x47, 0x43, 0x7c, - 0xda, 0x14, 0xa2, 0xd1, 0x40, 0xb8, 0x2c, 0x3f, 0xe9, 0x15, 0x8b, 0xc6, 0x25, 0x10, 0xe1, 0x97, - 0x20, 0xed, 0x9b, 0xef, 0x92, 0xd7, 0x1b, 0x6e, 0x5f, 0xc4, 0x81, 0x1f, 0xc9, 0xfb, 0xe6, 0x7b, - 0xe6, 0x4d, 0xa1, 0x11, 0x51, 0x93, 0xfc, 0x04, 0x0b, 0x30, 0xe7, 0x49, 0xea, 0xb7, 0x29, 0xe5, - 0x2c, 0x16, 0x8b, 0xe0, 0x25, 0x34, 0x1e, 0x53, 0x1e, 0x1e, 0xbb, 0x06, 0xd2, 0x6d, 0x24, 0x68, - 0x9e, 0xb8, 0xa6, 0xc6, 0xca, 0xaa, 0xdf, 0x95, 0x8d, 0xad, 0x18, 0x54, 0x30, 0x9c, 0x4a, 0xe8, - 0x1c, 0x10, 0x8c, 0xcf, 0x9a, 0x09, 0x46, 0x20, 0xc2, 0x97, 0xd1, 0x40, 0x24, 0xf3, 0x1b, 0x38, - 0x69, 0x06, 0x69, 0xdf, 0x02, 0x2c, 0xbe, 0x8e, 0x06, 0xe8, 0xfc, 0x6d, 0x45, 0x62, 0x3e, 0x75, - 0x38, 0x4c, 0x9c, 0x70, 0x7d, 0x3a, 0x5a, 0x46, 0x8a, 0x2e, 0xec, 0x27, 0xe8, 0x8a, 0xaf, 0x56, - 0x61, 0xec, 0xf4, 0x60, 0xaf, 0xd8, 0xbb, 0xd3, 0x5e, 0x51, 0xfd, 0x8d, 0x4c, 0x5c, 0xfb, 0xf1, - 0xcd, 0x78, 0xe0, 0x17, 0x96, 0x9b, 0xcb, 0x07, 0x8a, 0xb5, 0x06, 0x21, 0x60, 0xa4, 0x10, 0x2e, - 0xd9, 0x47, 0x0e, 0xe1, 0x92, 0xdb, 0x63, 0x08, 0x17, 0xf5, 0x7f, 0xe7, 0xbb, 0x1a, 0x5c, 0x1c, - 0x8a, 0xab, 0xf2, 0x8b, 0xf4, 0xbc, 0x43, 0x6b, 0x2f, 0xb9, 0xb1, 0x5d, 0x3b, 0x7b, 0x4f, 0xae, - 0xeb, 0x6c, 0xd4, 0xb8, 0x9a, 0x4c, 0x29, 0x66, 0x4a, 0x87, 0xd0, 0x40, 0xf9, 0x84, 0x4c, 0xe9, - 0xd1, 0xf4, 0x6a, 0x62, 0x01, 0xfc, 0x31, 0x34, 0x18, 0xe6, 0x7c, 0xef, 0x15, 0x02, 0x4d, 0x25, - 0xa4, 0x7a, 0x0f, 0x29, 0xf1, 0x67, 0x51, 0x9f, 0x94, 0xdf, 0xef, 0xda, 0x2e, 0x2c, 0x54, 0x26, - 0xc5, 0xf0, 0x85, 0xec, 0xec, 0x10, 0xcd, 0xed, 0xc7, 0x99, 0xe2, 0x65, 0x74, 0xaa, 0xea, 0x10, - 0x03, 0x6c, 0xa1, 0x66, 0x1e, 0xb6, 0x1d, 0x1e, 0x5c, 0x92, 0x0d, 0x60, 0x58, 0x3a, 0xda, 0x3e, - 0x9a, 0x2e, 0x6a, 0x1c, 0x2f, 0x30, 0x4a, 0x2a, 0x4e, 0xf7, 0x13, 0xac, 0x25, 0xb7, 0xc9, 0xe6, - 0x86, 0xed, 0x18, 0x2c, 0xfe, 0x22, 0xdf, 0x4f, 0x70, 0x41, 0xaf, 0x73, 0x94, 0xb8, 0x9f, 0x90, - 0x0b, 0x4d, 0xbc, 0x88, 0x86, 0x1e, 0x35, 0x04, 0xe0, 0x2f, 0x64, 0x53, 0x4c, 0x17, 0x8f, 0x6f, - 0xea, 0x86, 0x20, 0x8d, 0x4e, 0x6f, 0x4a, 0x1a, 0x9d, 0x6f, 0x65, 0x53, 0xec, 0x32, 0x8f, 0x75, - 0xba, 0x8b, 0x40, 0x18, 0x72, 0xba, 0x8b, 0x30, 0xd3, 0x88, 0x69, 0x68, 0x22, 0x51, 0x24, 0x31, - 0x4e, 0xdf, 0x8e, 0x89, 0x71, 0x7e, 0x2a, 0xd7, 0xcd, 0x6e, 0xf5, 0x44, 0xf6, 0x7b, 0x91, 0xfd, - 0x75, 0x34, 0x14, 0x48, 0x96, 0x27, 0x49, 0x1e, 0x09, 0x02, 0x8e, 0x32, 0x30, 0x94, 0x11, 0x88, - 0xf0, 0x15, 0xd6, 0xd6, 0x9a, 0xf9, 0x2e, 0x0b, 0xba, 0x37, 0xc2, 0xc3, 0xa9, 0xe9, 0x9e, 0x5e, - 0x77, 0xcd, 0x77, 0x89, 0x16, 0xa0, 0xd5, 0x7f, 0x9a, 0x4d, 0x34, 0xfe, 0x3d, 0xe9, 0xa3, 0x3d, - 0xf4, 0x51, 0x82, 0x10, 0x99, 0xd9, 0xf2, 0x89, 0x10, 0xf7, 0x20, 0xc4, 0x3f, 0xce, 0x26, 0x1a, - 0x79, 0x9f, 0x08, 0x71, 0x2f, 0xb3, 0xc5, 0x55, 0x34, 0xa8, 0xd9, 0x1b, 0xee, 0x34, 0x9c, 0x59, - 0xd8, 0x5c, 0x01, 0x13, 0xb5, 0x63, 0x6f, 0xb8, 0x75, 0x38, 0x8d, 0x68, 0x21, 0x81, 0xfa, 0xa7, - 0xd9, 0x2e, 0x66, 0xf0, 0x27, 0x82, 0x7f, 0x3f, 0x97, 0xc8, 0x5f, 0xce, 0x4a, 0x66, 0xf6, 0xc7, - 0x3a, 0x6f, 0x5c, 0xad, 0xb1, 0x46, 0x5a, 0x7a, 0x34, 0x6f, 0x9c, 0x0b, 0x50, 0x9e, 0x76, 0x26, - 0x24, 0x51, 0xbf, 0x9a, 0x8d, 0xf8, 0x19, 0x9c, 0xc8, 0x6e, 0xd7, 0xb2, 0x0b, 0xb4, 0x8e, 0xbb, - 0x4e, 0x9c, 0x48, 0x6e, 0xb7, 0x92, 0xfb, 0xfe, 0x6c, 0xc4, 0xcb, 0xe4, 0xf8, 0xa6, 0x90, 0xfa, - 0x6a, 0x36, 0xee, 0x31, 0x73, 0x7c, 0x35, 0xe9, 0x2a, 0x1a, 0xe4, 0x72, 0x08, 0x96, 0x0a, 0x36, - 0xef, 0x33, 0x20, 0x5c, 0xa0, 0x06, 0x04, 0xea, 0xf7, 0x64, 0x91, 0xec, 0xfd, 0x73, 0x4c, 0x75, - 0xe8, 0x97, 0xb3, 0xb2, 0xdf, 0xd3, 0xf1, 0xd5, 0x9f, 0x49, 0x84, 0x6a, 0x9d, 0x95, 0x06, 0x0f, - 0x9b, 0xd5, 0x2b, 0xdc, 0xc0, 0x07, 0x50, 0x4d, 0xa0, 0x50, 0xff, 0x4f, 0x36, 0xd1, 0x19, 0xeb, - 0xf8, 0x0a, 0xf0, 0x06, 0xdc, 0x8a, 0x37, 0xac, 0x70, 0x22, 0x87, 0x4b, 0x48, 0x3a, 0xfe, 0x62, - 0xd1, 0xee, 0x7d, 0x42, 0xfc, 0xf1, 0x84, 0xed, 0x1a, 0xc4, 0x12, 0x4c, 0x4c, 0xa1, 0x2d, 0x6e, - 0xdc, 0xfe, 0x45, 0x76, 0x27, 0xdf, 0xb5, 0xe3, 0xbc, 0xaa, 0xf6, 0x57, 0xf5, 0x4d, 0x88, 0xb1, - 0x42, 0x7b, 0x62, 0x98, 0xc5, 0x62, 0x6f, 0x33, 0x90, 0xf8, 0x22, 0xc6, 0xa9, 0xd4, 0x3f, 0xea, - 0x4d, 0x76, 0x9c, 0x3a, 0xbe, 0x22, 0x3c, 0x8f, 0xf2, 0x55, 0xdd, 0x5b, 0xe3, 0x9a, 0x0c, 0xaf, - 0x75, 0x6d, 0xdd, 0x5b, 0xd3, 0x00, 0x8a, 0xaf, 0xa0, 0x01, 0x4d, 0xdf, 0x10, 0x53, 0x87, 0xc3, - 0xc5, 0x8e, 0xa3, 0x6f, 0xf0, 0xfc, 0xf1, 0x01, 0x1a, 0xab, 0x41, 0x9e, 0x06, 0x76, 0xf3, 0x0d, - 0x41, 0xce, 0x59, 0x9e, 0x86, 0x20, 0x3b, 0xc3, 0x79, 0x94, 0x9f, 0xb2, 0x8d, 0x4d, 0x30, 0x66, - 0x19, 0x66, 0x95, 0xad, 0xd8, 0xc6, 0xa6, 0x06, 0x50, 0xfc, 0x03, 0x19, 0xd4, 0x3f, 0x47, 0x74, - 0x83, 0x8e, 0x90, 0xc1, 0x6e, 0xb6, 0x20, 0x6f, 0x1e, 0x8c, 0x2d, 0xc8, 0xf8, 0x1a, 0xab, 0x4c, - 0x54, 0x14, 0x5e, 0x3f, 0xbe, 0x85, 0x06, 0xa6, 0x75, 0x8f, 0xac, 0xda, 0xce, 0x26, 0x58, 0xb7, - 0x8c, 0x86, 0xd6, 0xa3, 0x92, 0xfe, 0xf8, 0x44, 0xec, 0x65, 0xac, 0xc1, 0x7f, 0x69, 0x41, 0x61, - 0x2a, 0x16, 0x9e, 0xbf, 0x6d, 0x28, 0x14, 0x0b, 0x4b, 0xd4, 0x16, 0xa4, 0x69, 0x0b, 0xae, 0x95, - 0x87, 0x93, 0xaf, 0x95, 0x61, 0xf7, 0x08, 0x16, 0x70, 0x90, 0x1d, 0x61, 0x04, 0x16, 0x7d, 0xb6, - 0x7b, 0x04, 0x28, 0x24, 0x47, 0xd0, 0x04, 0x12, 0xf5, 0x1b, 0xbd, 0x28, 0xd1, 0xcd, 0xe2, 0x44, - 0xc9, 0x4f, 0x94, 0x3c, 0x54, 0xf2, 0x72, 0x4c, 0xc9, 0x27, 0xe2, 0x8e, 0x3b, 0x1f, 0x50, 0x0d, - 0xff, 0xa1, 0x7c, 0xcc, 0xed, 0xef, 0x78, 0x9f, 0x2e, 0x43, 0xe9, 0xf5, 0xee, 0x28, 0xbd, 0x60, - 0x40, 0xf4, 0xed, 0x38, 0x20, 0xfa, 0x77, 0x3b, 0x20, 0x06, 0x52, 0x07, 0x44, 0xa8, 0x20, 0x83, - 0xa9, 0x0a, 0x52, 0xe1, 0x83, 0x06, 0x75, 0xcf, 0xbc, 0x73, 0x7e, 0x7b, 0xab, 0x38, 0x4a, 0x47, - 0x53, 0x62, 0xce, 0x1d, 0x60, 0xa1, 0x7e, 0x3d, 0xdf, 0xc5, 0x57, 0xf7, 0x50, 0x74, 0xe4, 0x06, - 0xca, 0x95, 0xda, 0x6d, 0xae, 0x1f, 0xa7, 0x04, 0x37, 0xe1, 0x94, 0x52, 0x94, 0x1a, 0xbf, 0x84, - 0x72, 0xa5, 0x7b, 0xb5, 0x68, 0xc4, 0xe1, 0xd2, 0xbd, 0x1a, 0xff, 0x92, 0xd4, 0xb2, 0xf7, 0x6a, - 0xf8, 0xe5, 0x30, 0xf4, 0xcf, 0x5a, 0xc7, 0x5a, 0xe7, 0x07, 0x45, 0x6e, 0x04, 0xeb, 0x5b, 0xda, - 0x34, 0x28, 0x8a, 0x1e, 0x17, 0x23, 0xb4, 0x11, 0x6d, 0xea, 0xdb, 0xbd, 0x36, 0xf5, 0xef, 0xa8, - 0x4d, 0x03, 0xbb, 0xd5, 0xa6, 0xc1, 0x5d, 0x68, 0x13, 0xda, 0x51, 0x9b, 0x86, 0xf6, 0xaf, 0x4d, - 0x6d, 0x34, 0x11, 0x8f, 0xaf, 0x10, 0x68, 0x84, 0x86, 0x70, 0x1c, 0xcb, 0x0d, 0x4b, 0xe0, 0xe9, - 0xbf, 0xc3, 0xb0, 0x75, 0x96, 0x67, 0x31, 0x9a, 0xa5, 0x50, 0x4b, 0x28, 0xad, 0xfe, 0x42, 0x36, - 0x3d, 0x2c, 0xc4, 0xd1, 0x9c, 0xe2, 0xbe, 0x23, 0x51, 0x4a, 0x79, 0xd9, 0x21, 0x2a, 0x5d, 0xca, - 0x11, 0xb6, 0x49, 0x32, 0xfb, 0x5a, 0x26, 0x2d, 0x56, 0xc5, 0xbe, 0x24, 0xf6, 0xe1, 0xb8, 0xb1, - 0x1a, 0x58, 0xcf, 0xbb, 0xb2, 0x95, 0x5a, 0x34, 0x6d, 0x5f, 0xee, 0x11, 0xd3, 0xf6, 0xfd, 0x46, - 0x06, 0x9d, 0xba, 0xdd, 0x59, 0x21, 0xdc, 0x38, 0x2d, 0x68, 0xc6, 0x3b, 0x08, 0x51, 0x30, 0x37, - 0x62, 0xc9, 0x80, 0x11, 0xcb, 0x47, 0xc4, 0x38, 0x13, 0x91, 0x02, 0x93, 0x21, 0x35, 0x33, 0x60, - 0xb9, 0xe0, 0x9b, 0x58, 0xae, 0x77, 0x56, 0x48, 0x3d, 0x66, 0xc9, 0x22, 0x70, 0x9f, 0x78, 0x85, - 0x19, 0xaf, 0x3f, 0xaa, 0xd1, 0xc8, 0xcf, 0x65, 0x53, 0x43, 0x7b, 0x1c, 0xd9, 0xcc, 0x0a, 0x9f, - 0x4e, 0xec, 0x95, 0x68, 0x86, 0x85, 0x04, 0x92, 0x08, 0xc7, 0x24, 0x2e, 0xc9, 0x02, 0x3b, 0xe2, - 0xf9, 0x3e, 0xde, 0x57, 0x81, 0xfd, 0x6e, 0x26, 0x35, 0x04, 0xcb, 0x51, 0x15, 0x98, 0xfa, 0x5b, - 0x59, 0x3f, 0xf2, 0xcb, 0xbe, 0x3e, 0xe1, 0x2a, 0x1a, 0xe4, 0xe1, 0xed, 0x65, 0xdb, 0x5a, 0x7e, - 0x95, 0x07, 0x57, 0xc3, 0x01, 0x01, 0x5d, 0xe6, 0xfd, 0xc8, 0x14, 0x41, 0xa2, 0x47, 0x58, 0xe6, - 0x4d, 0x0e, 0xa5, 0xf4, 0x02, 0x09, 0x5d, 0xc8, 0x67, 0x1e, 0x9a, 0x1e, 0xec, 0x0a, 0x68, 0x5f, - 0xe6, 0xd8, 0x42, 0x4e, 0x1e, 0x9a, 0x1e, 0xdb, 0x13, 0x04, 0x68, 0xba, 0x48, 0xd7, 0xc2, 0x6c, - 0x66, 0x7c, 0x91, 0x76, 0x79, 0x52, 0x37, 0xee, 0xcc, 0x75, 0x15, 0x0d, 0x72, 0x83, 0x55, 0x6e, - 0x66, 0xc2, 0x5b, 0xcb, 0x4d, 0x5c, 0xa1, 0xb5, 0x01, 0x01, 0xe5, 0xa8, 0x91, 0xd5, 0xd0, 0xb0, - 0x0e, 0x38, 0x3a, 0x00, 0xd1, 0x38, 0x46, 0xdd, 0xce, 0xc6, 0x03, 0xd0, 0x1c, 0xdf, 0x43, 0xc1, - 0x15, 0xd9, 0x58, 0x0d, 0x2c, 0x34, 0x61, 0xc3, 0x25, 0xda, 0xca, 0xb2, 0x7d, 0xd7, 0x75, 0x34, - 0x70, 0x9b, 0x6c, 0x32, 0xbb, 0xca, 0xbe, 0xd0, 0x14, 0x77, 0x9d, 0xc3, 0xc4, 0x1b, 0x4d, 0x9f, - 0x4e, 0xfd, 0xf5, 0x6c, 0x3c, 0xb4, 0xce, 0xf1, 0x15, 0xf6, 0x47, 0x51, 0x3f, 0x88, 0xb2, 0xe2, - 0x5f, 0xa9, 0x83, 0x00, 0x41, 0xdc, 0xb2, 0x85, 0xaf, 0x4f, 0xa6, 0xfe, 0x58, 0x5f, 0x34, 0xde, - 0xd2, 0xf1, 0x95, 0xde, 0x27, 0xd0, 0xd0, 0xb4, 0x6d, 0xb9, 0xa6, 0xeb, 0x11, 0xab, 0xe1, 0x2b, - 0xec, 0x63, 0x74, 0xc3, 0xd2, 0x08, 0xc1, 0xa2, 0xe7, 0x8d, 0x40, 0xfd, 0x28, 0xca, 0x8b, 0x9f, - 0x47, 0x83, 0x20, 0x72, 0xb0, 0x43, 0x16, 0xd2, 0xc4, 0xae, 0x50, 0x60, 0xd4, 0x08, 0x39, 0x24, - 0xc5, 0x77, 0xd0, 0xc0, 0xf4, 0x9a, 0xd9, 0x34, 0x1c, 0x62, 0xf1, 0x7c, 0xe8, 0x4f, 0x26, 0x47, - 0xc7, 0x9a, 0x84, 0x7f, 0x81, 0x96, 0x35, 0xa7, 0xc1, 0x8b, 0x49, 0xbe, 0x47, 0x1c, 0x36, 0xf1, - 0x37, 0xb3, 0x08, 0x85, 0x05, 0xf0, 0x13, 0x28, 0x1b, 0x24, 0xe2, 0x01, 0x33, 0x10, 0x49, 0x83, - 0xb2, 0x30, 0x15, 0xf3, 0xb1, 0x9d, 0xdd, 0x71, 0x6c, 0xdf, 0x41, 0x7d, 0xec, 0x46, 0x09, 0x2c, - 0xb5, 0x85, 0x10, 0x30, 0xa9, 0x0d, 0x9e, 0x04, 0x7a, 0x76, 0x58, 0x84, 0x9d, 0x9d, 0x64, 0xf5, - 0xcc, 0x98, 0x4d, 0x34, 0x50, 0x2f, 0xfc, 0x85, 0x2f, 0xa1, 0x3c, 0x48, 0x31, 0x03, 0xe7, 0x44, - 0x70, 0x13, 0x8d, 0xc8, 0x0f, 0xf0, 0xb4, 0x9b, 0xa6, 0x6d, 0xcb, 0xa3, 0x55, 0x43, 0xab, 0x87, - 0xb9, 0x5c, 0x38, 0x4c, 0x92, 0x0b, 0x87, 0xa9, 0xff, 0x3c, 0x9b, 0x10, 0x09, 0xec, 0xf8, 0x0e, - 0x93, 0x17, 0x11, 0x02, 0x47, 0x66, 0x2a, 0x4f, 0xdf, 0x05, 0x02, 0x46, 0x09, 0x30, 0x02, 0xb5, - 0x95, 0xb6, 0xf5, 0x21, 0xb1, 0xfa, 0xdb, 0x99, 0x58, 0xf8, 0xa8, 0x7d, 0xc9, 0x51, 0xdc, 0xf5, - 0x64, 0x1f, 0x71, 0x9b, 0xe8, 0xf7, 0x45, 0x6e, 0x6f, 0x7d, 0x21, 0x7f, 0xcb, 0x01, 0xec, 0xfc, - 0x0e, 0xf3, 0x5b, 0xbe, 0x91, 0x4d, 0x0a, 0xa6, 0x75, 0x34, 0x55, 0x3c, 0xcc, 0x35, 0x9e, 0xdf, - 0x43, 0xae, 0xf1, 0xb7, 0xd1, 0x58, 0x24, 0xc4, 0x14, 0xcf, 0x8e, 0x75, 0xa9, 0x7b, 0xac, 0xaa, - 0x74, 0x17, 0x78, 0x89, 0x4c, 0xfd, 0xbf, 0x99, 0xee, 0x01, 0xc6, 0x0e, 0x5d, 0x75, 0x12, 0x04, - 0x90, 0xfb, 0x8b, 0x11, 0xc0, 0x01, 0x1c, 0x33, 0x8f, 0xb6, 0x00, 0x3e, 0x20, 0x93, 0xc7, 0xfb, - 0x2d, 0x80, 0x1f, 0xcb, 0xec, 0x18, 0x1f, 0xee, 0xb0, 0x65, 0xa0, 0xfe, 0xc7, 0x4c, 0x62, 0x1c, - 0xb7, 0x7d, 0xb5, 0xeb, 0x65, 0xd4, 0xc7, 0xcc, 0x56, 0x78, 0xab, 0x84, 0xc8, 0xf7, 0x14, 0x9a, - 0x52, 0x9e, 0x97, 0xc1, 0xf3, 0xa8, 0x9f, 0xb5, 0xc1, 0x88, 0x66, 0x88, 0x4c, 0x68, 0xa7, 0x91, - 0x36, 0x39, 0x72, 0xb4, 0xfa, 0x9b, 0x99, 0x58, 0x58, 0xb9, 0x43, 0xfc, 0xb6, 0x70, 0xaa, 0xce, - 0xed, 0x7e, 0xaa, 0x56, 0xff, 0x30, 0x9b, 0x1c, 0xd5, 0xee, 0x10, 0x3f, 0xe4, 0x20, 0xae, 0xab, - 0x1e, 0x6d, 0xdd, 0x5a, 0x46, 0xa3, 0xb2, 0x2c, 0xf8, 0xb2, 0x75, 0x31, 0x39, 0xb6, 0x5f, 0x4a, - 0x2b, 0x22, 0x3c, 0xd4, 0xf7, 0x32, 0xf1, 0x80, 0x7c, 0x87, 0x3e, 0x3f, 0x3d, 0x9a, 0xb6, 0xc8, - 0x9f, 0xf2, 0x01, 0x59, 0x6b, 0x0e, 0xe2, 0x53, 0x3e, 0x20, 0xab, 0xc6, 0xa3, 0x7d, 0xca, 0x4f, - 0x67, 0xd3, 0xe2, 0x19, 0x1e, 0xfa, 0x07, 0x7d, 0x4a, 0x14, 0x32, 0x6b, 0x19, 0xff, 0xb4, 0x27, - 0xd2, 0x02, 0x08, 0xa6, 0xf0, 0x8c, 0xf1, 0x79, 0xb4, 0x31, 0x9e, 0x28, 0xac, 0x0f, 0x88, 0x22, - 0x1f, 0x0d, 0x61, 0x7d, 0x40, 0x86, 0xca, 0x07, 0x4f, 0x58, 0xbf, 0x9a, 0xdd, 0x6d, 0x10, 0xcd, - 0x13, 0xe1, 0xc5, 0x84, 0xf7, 0xe5, 0x6c, 0x3c, 0xb8, 0xeb, 0xa1, 0x8b, 0x69, 0x16, 0xf5, 0xf1, - 0x30, 0xb3, 0xa9, 0xc2, 0x61, 0xf8, 0xb4, 0x1d, 0x0d, 0xff, 0x8e, 0x9b, 0x88, 0x3f, 0x94, 0xec, - 0x4e, 0x24, 0x8c, 0x56, 0xfd, 0xd3, 0x4c, 0x24, 0x12, 0xea, 0xa1, 0x5c, 0x21, 0x3c, 0xd2, 0x92, - 0x84, 0x5f, 0xf1, 0x2f, 0x33, 0xf3, 0x91, 0x24, 0x93, 0xc1, 0xf7, 0x94, 0x89, 0xa7, 0x9b, 0xcd, - 0x68, 0x79, 0xee, 0x73, 0xff, 0xeb, 0x59, 0x34, 0x1e, 0x23, 0xc5, 0x97, 0xa4, 0x28, 0x34, 0x70, - 0x2d, 0x19, 0x31, 0xce, 0x66, 0xf1, 0x68, 0xf6, 0x70, 0x93, 0x7a, 0x09, 0xe5, 0xcb, 0xfa, 0x26, - 0xfb, 0xb6, 0x5e, 0xc6, 0xd2, 0xd0, 0x37, 0xc5, 0x1b, 0x37, 0xc0, 0xe3, 0x15, 0x74, 0x86, 0xbd, - 0x87, 0x98, 0xb6, 0xb5, 0x6c, 0xb6, 0x48, 0xc5, 0x5a, 0x30, 0x9b, 0x4d, 0xd3, 0xe5, 0x8f, 0x66, - 0x57, 0xb7, 0xb7, 0x8a, 0x97, 0x3d, 0xdb, 0xd3, 0x9b, 0x75, 0xe2, 0x93, 0xd5, 0x3d, 0xb3, 0x45, - 0xea, 0xa6, 0x55, 0x6f, 0x01, 0xa5, 0xc0, 0x32, 0x99, 0x15, 0xae, 0xb0, 0x2c, 0x98, 0xb5, 0x86, - 0x6e, 0x59, 0xc4, 0xa8, 0x58, 0x53, 0x9b, 0x1e, 0x61, 0x8f, 0x6d, 0x39, 0x76, 0x25, 0xc8, 0x7c, - 0xaf, 0x19, 0x9a, 0x32, 0x5e, 0xa1, 0x04, 0x5a, 0x42, 0x21, 0xf5, 0xd7, 0xf2, 0x09, 0x41, 0x70, - 0x8f, 0x90, 0xfa, 0xf8, 0x3d, 0x9d, 0xdf, 0xa1, 0xa7, 0xaf, 0xa1, 0x7e, 0x1e, 0x67, 0x92, 0x3f, - 0x30, 0x80, 0xb1, 0xf8, 0x03, 0x06, 0x12, 0x5f, 0x68, 0x38, 0x15, 0x6e, 0xa2, 0x89, 0x65, 0xda, - 0x4d, 0xc9, 0x9d, 0xd9, 0xf7, 0x08, 0x9d, 0xd9, 0x85, 0x1f, 0x7e, 0x0b, 0x9d, 0x03, 0x6c, 0x42, - 0xb7, 0xf6, 0x43, 0x55, 0x10, 0x99, 0x89, 0x55, 0x95, 0xdc, 0xb9, 0x69, 0xe5, 0xf1, 0xa7, 0xd0, - 0x70, 0x30, 0x40, 0x4c, 0xe2, 0xf2, 0x97, 0x8b, 0x2e, 0xe3, 0x8c, 0x85, 0x3d, 0xa3, 0x60, 0x30, - 0xd1, 0x92, 0x43, 0x67, 0x49, 0xbc, 0xd4, 0xff, 0x90, 0xe9, 0x16, 0xf6, 0xf8, 0xd0, 0x67, 0xe5, - 0x57, 0x50, 0xbf, 0xc1, 0x3e, 0x8a, 0xeb, 0x54, 0xf7, 0xc0, 0xc8, 0x8c, 0x54, 0xf3, 0xcb, 0xa8, - 0x7f, 0x90, 0xe9, 0x1a, 0x6d, 0xf9, 0xa8, 0x7f, 0xde, 0x97, 0x73, 0x29, 0x9f, 0xc7, 0x27, 0xd1, - 0x2b, 0xa8, 0x60, 0x86, 0x91, 0x7c, 0xeb, 0x61, 0x78, 0x27, 0x6d, 0x4c, 0x80, 0xc3, 0xe8, 0xba, - 0x89, 0xce, 0xfa, 0x86, 0x85, 0x8e, 0x6f, 0x81, 0xe5, 0xd6, 0x3b, 0x8e, 0xc9, 0xc6, 0xa5, 0x76, - 0xda, 0x8d, 0x98, 0x67, 0xb9, 0x77, 0x1c, 0x93, 0x56, 0xa0, 0x7b, 0x6b, 0xc4, 0xd2, 0xeb, 0x1b, - 0xb6, 0xb3, 0x0e, 0xb1, 0x35, 0xd9, 0xe0, 0xd4, 0xc6, 0x18, 0xfc, 0x9e, 0x0f, 0xc6, 0x4f, 0xa1, - 0x91, 0xd5, 0x66, 0x87, 0x04, 0xd1, 0x0c, 0xd9, 0x5b, 0x9f, 0x36, 0x4c, 0x81, 0xc1, 0x0b, 0xc9, - 0x05, 0x84, 0x80, 0xc8, 0x83, 0x58, 0xd8, 0xf0, 0xb0, 0xa7, 0x0d, 0x52, 0xc8, 0x32, 0xef, 0xae, - 0x09, 0xa6, 0xd5, 0x4c, 0x48, 0xf5, 0xa6, 0x6d, 0xad, 0xd6, 0x3d, 0xe2, 0xb4, 0xa0, 0xa1, 0x60, - 0x9c, 0xa8, 0x9d, 0x05, 0x0a, 0x78, 0x3a, 0x71, 0xe7, 0x6d, 0x6b, 0x75, 0x99, 0x38, 0x2d, 0xda, - 0xd4, 0xab, 0x08, 0xf3, 0xa6, 0x3a, 0x70, 0xe9, 0xc1, 0x3e, 0x0e, 0xec, 0x14, 0x35, 0xfe, 0x11, - 0xec, 0x36, 0x04, 0x3e, 0xac, 0x88, 0x86, 0x58, 0x48, 0x37, 0x26, 0x34, 0x30, 0x55, 0xd4, 0x10, - 0x03, 0x81, 0xbc, 0xce, 0x22, 0x6e, 0xbd, 0xc0, 0xac, 0xa6, 0x35, 0xfe, 0x4b, 0xfd, 0xdb, 0xd9, - 0xb4, 0x40, 0xc9, 0x47, 0xf5, 0x8d, 0x03, 0xcf, 0x21, 0xc4, 0x33, 0x4a, 0xd2, 0xcf, 0x8d, 0x18, - 0xb4, 0x86, 0x98, 0x14, 0x1e, 0x42, 0x59, 0xf5, 0x0b, 0xd9, 0xb4, 0x50, 0xcf, 0xfb, 0x12, 0x4e, - 0xb8, 0xee, 0x64, 0xf7, 0xb0, 0xee, 0x1c, 0xbe, 0x38, 0x92, 0x74, 0xe5, 0x68, 0xbf, 0x87, 0x1d, - 0xa0, 0x70, 0x7e, 0x24, 0x9b, 0x1a, 0x60, 0xfb, 0x44, 0x3a, 0xea, 0x17, 0xb3, 0xa9, 0x01, 0xc2, - 0x8f, 0xe5, 0x50, 0x4a, 0xd4, 0x96, 0x93, 0xb1, 0xc4, 0x28, 0x9e, 0x59, 0x60, 0xe1, 0x1d, 0x6f, - 0x9b, 0x96, 0x81, 0x1f, 0x43, 0x67, 0xee, 0xd4, 0x66, 0xb4, 0xfa, 0xed, 0xca, 0x62, 0xb9, 0x7e, - 0x67, 0xb1, 0x56, 0x9d, 0x99, 0xae, 0xcc, 0x56, 0x66, 0xca, 0x85, 0x1e, 0x7c, 0x0a, 0x8d, 0x85, - 0xa8, 0xb9, 0x3b, 0x0b, 0xa5, 0xc5, 0x42, 0x06, 0x8f, 0xa3, 0x91, 0x10, 0x38, 0xb5, 0xb4, 0x5c, - 0xc8, 0x3e, 0xf3, 0x34, 0x1a, 0x82, 0xf5, 0xb5, 0x04, 0xdc, 0xf1, 0x30, 0x1a, 0x58, 0x9a, 0xaa, - 0xcd, 0x68, 0x77, 0x81, 0x09, 0x42, 0x7d, 0xe5, 0x99, 0x45, 0xca, 0x30, 0xf3, 0xcc, 0xff, 0xca, - 0x20, 0x54, 0x9b, 0x5d, 0xae, 0x72, 0xc2, 0x21, 0xd4, 0x5f, 0x59, 0xbc, 0x5b, 0x9a, 0xaf, 0x50, - 0xba, 0x01, 0x94, 0x5f, 0xaa, 0xce, 0xd0, 0x1a, 0x06, 0x51, 0xef, 0xf4, 0xfc, 0x52, 0x6d, 0xa6, - 0x90, 0xa5, 0x40, 0x6d, 0xa6, 0x54, 0x2e, 0xe4, 0x28, 0xf0, 0x9e, 0x56, 0x59, 0x9e, 0x29, 0xe4, - 0xe9, 0x9f, 0xf3, 0xb5, 0xe5, 0xd2, 0x72, 0xa1, 0x97, 0xfe, 0x39, 0x0b, 0x7f, 0xf6, 0x51, 0x66, - 0xb5, 0x99, 0x65, 0xf8, 0xd1, 0x4f, 0x9b, 0x30, 0xeb, 0xff, 0x1a, 0xa0, 0x28, 0xca, 0xba, 0x5c, - 0xd1, 0x0a, 0x83, 0xf4, 0x07, 0x65, 0x49, 0x7f, 0x20, 0xda, 0x38, 0x6d, 0x66, 0x61, 0xe9, 0xee, - 0x4c, 0x61, 0x88, 0xf2, 0x5a, 0xb8, 0x4d, 0xc1, 0xc3, 0xf4, 0x4f, 0x6d, 0x81, 0xfe, 0x39, 0x42, - 0x39, 0x69, 0x33, 0xa5, 0xf9, 0x6a, 0x69, 0x79, 0xae, 0x30, 0x4a, 0xdb, 0x03, 0x3c, 0xc7, 0x58, - 0xc9, 0xc5, 0xd2, 0xc2, 0x4c, 0xa1, 0xc0, 0x69, 0xca, 0xf3, 0x95, 0xc5, 0xdb, 0x85, 0x71, 0x68, - 0xc8, 0x5b, 0x0b, 0xf0, 0x03, 0xd3, 0x02, 0xf0, 0xd7, 0xa9, 0x67, 0x3e, 0x83, 0xfa, 0x96, 0x6a, - 0x60, 0x39, 0x73, 0x0e, 0x9d, 0x5a, 0xaa, 0xd5, 0x97, 0xdf, 0xaa, 0xce, 0x44, 0xe4, 0x3d, 0x8e, - 0x46, 0x7c, 0xc4, 0x7c, 0x65, 0xf1, 0xce, 0x9b, 0x4c, 0xda, 0x3e, 0x68, 0xa1, 0x34, 0xbd, 0x54, - 0x2b, 0x64, 0x69, 0xaf, 0xf8, 0xa0, 0x7b, 0x95, 0xc5, 0xf2, 0xd2, 0xbd, 0x5a, 0x21, 0xf7, 0xcc, - 0x03, 0x3f, 0xe3, 0xd2, 0x92, 0x63, 0xae, 0x9a, 0x16, 0xbe, 0x80, 0x1e, 0x2b, 0xcf, 0xdc, 0xad, - 0x4c, 0xcf, 0xd4, 0x97, 0xb4, 0xca, 0xad, 0xca, 0x62, 0xa4, 0xa6, 0x33, 0x68, 0x5c, 0x46, 0x97, - 0xaa, 0x95, 0x42, 0x06, 0x9f, 0x45, 0x58, 0x06, 0xbf, 0x5e, 0x5a, 0x98, 0x2d, 0x64, 0xb1, 0x82, - 0x4e, 0xcb, 0xf0, 0xca, 0xe2, 0xf2, 0x9d, 0xc5, 0x99, 0x42, 0xee, 0x99, 0x9f, 0xc8, 0xa0, 0x33, - 0x89, 0x0e, 0x94, 0x58, 0x45, 0x17, 0x67, 0xe6, 0x4b, 0xb5, 0xe5, 0xca, 0x74, 0x6d, 0xa6, 0xa4, - 0x4d, 0xcf, 0xd5, 0xa7, 0x4b, 0xcb, 0x33, 0xb7, 0x96, 0xb4, 0xb7, 0xea, 0xb7, 0x66, 0x16, 0x67, - 0xb4, 0xd2, 0x7c, 0xa1, 0x07, 0x3f, 0x85, 0x8a, 0x29, 0x34, 0xb5, 0x99, 0xe9, 0x3b, 0x5a, 0x65, - 0xf9, 0xad, 0x42, 0x06, 0x3f, 0x89, 0x2e, 0xa4, 0x12, 0xd1, 0xdf, 0x85, 0x2c, 0xbe, 0x88, 0x26, - 0xd2, 0x48, 0xde, 0x98, 0x2f, 0xe4, 0x9e, 0xf9, 0xe1, 0x0c, 0xc2, 0x71, 0x0f, 0x38, 0xfc, 0x04, - 0x3a, 0x4f, 0xf5, 0xa2, 0x9e, 0xde, 0xc0, 0x27, 0xd1, 0x85, 0x44, 0x0a, 0xa1, 0x79, 0x45, 0xf4, - 0x78, 0x0a, 0x09, 0x6f, 0xdc, 0x79, 0xa4, 0x24, 0x13, 0xd0, 0xa6, 0x4d, 0x95, 0xdf, 0xfb, 0x4f, - 0x17, 0x7b, 0xde, 0xfb, 0xe6, 0xc5, 0xcc, 0xef, 0x7c, 0xf3, 0x62, 0xe6, 0x0f, 0xbf, 0x79, 0x31, - 0xf3, 0xa9, 0xeb, 0x7b, 0x71, 0x10, 0x64, 0xa3, 0x7f, 0xa5, 0x0f, 0x5c, 0x61, 0x6e, 0xfc, 0xbf, - 0x00, 0x00, 0x00, 0xff, 0xff, 0x0d, 0xb0, 0x44, 0xda, 0x20, 0x36, 0x01, 0x00, + 0x2a, 0xa9, 0x8a, 0x1f, 0xe5, 0xc4, 0x71, 0xd9, 0x96, 0xed, 0xa4, 0x6c, 0xcb, 0x8e, 0x2b, 0x76, + 0xe4, 0x54, 0xe8, 0x28, 0x49, 0xd9, 0x4e, 0xca, 0x95, 0x4a, 0x1c, 0xc8, 0x51, 0xca, 0xf9, 0x03, + 0x95, 0x54, 0x39, 0x15, 0x55, 0xac, 0x38, 0x4e, 0x2a, 0xd5, 0x5f, 0xf7, 0xcc, 0x74, 0xcf, 0x63, + 0xf1, 0xa4, 0x41, 0xe8, 0xf0, 0xcf, 0x1d, 0xf6, 0xfb, 0xbe, 0xfe, 0xba, 0xe7, 0xeb, 0xaf, 0xdf, + 0xdf, 0x03, 0x5d, 0xf1, 0x48, 0x93, 0xb4, 0x6d, 0xc7, 0xbb, 0xd6, 0x24, 0xab, 0x7a, 0x63, 0xf3, + 0x9a, 0xb7, 0xd9, 0x26, 0xee, 0x35, 0xf2, 0x80, 0x58, 0x9e, 0xff, 0xdf, 0x64, 0xdb, 0xb1, 0x3d, + 0x1b, 0xf7, 0xb1, 0x5f, 0x13, 0xa7, 0x57, 0xed, 0x55, 0x1b, 0x40, 0xd7, 0xe8, 0x5f, 0x0c, 0x3b, + 0x71, 0x7e, 0xd5, 0xb6, 0x57, 0x9b, 0xe4, 0x1a, 0xfc, 0x5a, 0xe9, 0xdc, 0xbf, 0xe6, 0x7a, 0x4e, + 0xa7, 0xe1, 0x71, 0x6c, 0x31, 0x8a, 0xf5, 0xcc, 0x16, 0x71, 0x3d, 0xbd, 0xd5, 0xe6, 0x04, 0x17, + 0xa3, 0x04, 0x1b, 0x8e, 0xde, 0x6e, 0x13, 0x87, 0x57, 0x3e, 0xf1, 0x64, 0x72, 0x3b, 0xe1, 0x5f, + 0x4e, 0xf2, 0x6c, 0x32, 0x89, 0xcf, 0x28, 0xc2, 0x51, 0xfd, 0xe1, 0x2c, 0x1a, 0x58, 0x20, 0x9e, + 0x6e, 0xe8, 0x9e, 0x8e, 0xcf, 0xa3, 0xde, 0x8a, 0x65, 0x90, 0x87, 0x4a, 0xe6, 0x89, 0xcc, 0xe5, + 0xdc, 0x54, 0xdf, 0xf6, 0x56, 0x31, 0x4b, 0x4c, 0x8d, 0x01, 0xf1, 0x05, 0x94, 0x5f, 0xde, 0x6c, + 0x13, 0x25, 0xfb, 0x44, 0xe6, 0xf2, 0xe0, 0xd4, 0xe0, 0xf6, 0x56, 0xb1, 0x17, 0x64, 0xa1, 0x01, + 0x18, 0x3f, 0x89, 0xb2, 0x95, 0xb2, 0x92, 0x03, 0xe4, 0xf8, 0xf6, 0x56, 0x71, 0xa4, 0x63, 0x1a, + 0x57, 0xed, 0x96, 0xe9, 0x91, 0x56, 0xdb, 0xdb, 0xd4, 0xb2, 0x95, 0x32, 0xbe, 0x84, 0xf2, 0xd3, + 0xb6, 0x41, 0x94, 0x3c, 0x10, 0xe1, 0xed, 0xad, 0xe2, 0x68, 0xc3, 0x36, 0x88, 0x40, 0x05, 0x78, + 0xfc, 0x1a, 0xca, 0x2f, 0x9b, 0x2d, 0xa2, 0xf4, 0x3e, 0x91, 0xb9, 0x3c, 0x74, 0x7d, 0x62, 0x92, + 0x49, 0x65, 0xd2, 0x97, 0xca, 0xe4, 0xb2, 0x2f, 0xb6, 0xa9, 0xc2, 0x7b, 0x5b, 0xc5, 0x9e, 0xed, + 0xad, 0x62, 0x9e, 0x4a, 0xf2, 0xcb, 0xdf, 0x28, 0x66, 0x34, 0x28, 0x89, 0x5f, 0x46, 0x43, 0xd3, + 0xcd, 0x8e, 0xeb, 0x11, 0x67, 0x51, 0x6f, 0x11, 0xa5, 0x0f, 0x2a, 0x9c, 0xd8, 0xde, 0x2a, 0x9e, + 0x6d, 0x30, 0x70, 0xdd, 0xd2, 0x5b, 0x62, 0xc5, 0x22, 0xb9, 0xfa, 0xab, 0x19, 0x34, 0x56, 0x23, + 0xae, 0x6b, 0xda, 0x56, 0x20, 0x9b, 0x0f, 0xa3, 0x41, 0x0e, 0xaa, 0x94, 0x41, 0x3e, 0x83, 0x53, + 0xfd, 0xdb, 0x5b, 0xc5, 0x9c, 0x6b, 0x1a, 0x5a, 0x88, 0xc1, 0x1f, 0x45, 0xfd, 0xf7, 0x4c, 0x6f, + 0x6d, 0x61, 0xb6, 0xc4, 0xe5, 0x74, 0x76, 0x7b, 0xab, 0x88, 0x37, 0x4c, 0x6f, 0xad, 0xde, 0xba, + 0xaf, 0x0b, 0x15, 0xfa, 0x64, 0x78, 0x1e, 0x15, 0xaa, 0x8e, 0xf9, 0x40, 0xf7, 0xc8, 0x6d, 0xb2, + 0x59, 0xb5, 0x9b, 0x66, 0x63, 0x93, 0x4b, 0xf1, 0x89, 0xed, 0xad, 0xe2, 0xf9, 0x36, 0xc3, 0xd5, + 0xd7, 0xc9, 0x66, 0xbd, 0x0d, 0x58, 0x81, 0x49, 0xac, 0xa4, 0xfa, 0xb5, 0x5e, 0x34, 0x7c, 0xc7, + 0x25, 0x4e, 0xd0, 0xee, 0x4b, 0x28, 0x4f, 0x7f, 0xf3, 0x26, 0x83, 0xcc, 0x3b, 0x2e, 0x71, 0x44, + 0x99, 0x53, 0x3c, 0xbe, 0x82, 0x7a, 0xe7, 0xed, 0x55, 0xd3, 0xe2, 0xcd, 0x3e, 0xb5, 0xbd, 0x55, + 0x1c, 0x6b, 0x52, 0x80, 0x40, 0xc9, 0x28, 0xf0, 0x27, 0xd1, 0x70, 0xa5, 0x45, 0x75, 0xc8, 0xb6, + 0x74, 0xcf, 0x76, 0x78, 0x6b, 0x41, 0xba, 0xa6, 0x00, 0x17, 0x0a, 0x4a, 0xf4, 0xf8, 0x25, 0x84, + 0x4a, 0xf7, 0x6a, 0x9a, 0xdd, 0x24, 0x25, 0x6d, 0x91, 0x2b, 0x03, 0x94, 0xd6, 0x37, 0xdc, 0xba, + 0x63, 0x37, 0x49, 0x5d, 0x77, 0xc4, 0x6a, 0x05, 0x6a, 0x3c, 0x83, 0x46, 0x4b, 0x8d, 0x06, 0x71, + 0x5d, 0x8d, 0x7c, 0xae, 0x43, 0x5c, 0xcf, 0x55, 0x7a, 0x9f, 0xc8, 0x5d, 0x1e, 0x9c, 0xba, 0xb0, + 0xbd, 0x55, 0x7c, 0x4c, 0x07, 0x4c, 0xdd, 0xe1, 0x28, 0x81, 0x45, 0xa4, 0x10, 0x9e, 0x42, 0x23, + 0xa5, 0x77, 0x3b, 0x0e, 0xa9, 0x18, 0xc4, 0xf2, 0x4c, 0x6f, 0x93, 0x6b, 0xc8, 0xf9, 0xed, 0xad, + 0xa2, 0xa2, 0x53, 0x44, 0xdd, 0xe4, 0x18, 0x81, 0x89, 0x5c, 0x04, 0x2f, 0xa1, 0xf1, 0x5b, 0xd3, + 0xd5, 0x1a, 0x71, 0x1e, 0x98, 0x0d, 0x52, 0x6a, 0x34, 0xec, 0x8e, 0xe5, 0x29, 0xfd, 0xc0, 0xe7, + 0xc9, 0xed, 0xad, 0xe2, 0x85, 0xd5, 0x46, 0xbb, 0xee, 0x32, 0x6c, 0x5d, 0x67, 0x68, 0x81, 0x59, + 0xbc, 0x2c, 0xfe, 0x14, 0x1a, 0x59, 0x76, 0xa8, 0x16, 0x1a, 0x65, 0x42, 0xe1, 0xca, 0x00, 0xe8, + 0xff, 0xd9, 0x49, 0x3e, 0x01, 0x31, 0xa8, 0xdf, 0xb3, 0xac, 0xb1, 0x1e, 0x2b, 0x50, 0x37, 0x00, + 0x27, 0x36, 0x56, 0x62, 0x85, 0x09, 0x52, 0xe8, 0xc7, 0x9b, 0x0e, 0x31, 0x62, 0xda, 0x36, 0x08, + 0x6d, 0xbe, 0xb2, 0xbd, 0x55, 0xfc, 0xb0, 0xc3, 0x69, 0xea, 0x5d, 0xd5, 0x2e, 0x95, 0x15, 0x9e, + 0x41, 0x03, 0x54, 0x9b, 0x6e, 0x9b, 0x96, 0xa1, 0xa0, 0x27, 0x32, 0x97, 0x47, 0xaf, 0x17, 0xfc, + 0xd6, 0xfb, 0xf0, 0xa9, 0x73, 0xdb, 0x5b, 0xc5, 0x53, 0x54, 0x07, 0xeb, 0xeb, 0xa6, 0x25, 0x4e, + 0x11, 0x41, 0x51, 0xf5, 0x4f, 0xf3, 0x68, 0x94, 0x0a, 0x47, 0xd0, 0xe3, 0x12, 0x1d, 0x92, 0x14, + 0x42, 0x47, 0xa8, 0xdb, 0xd6, 0x1b, 0x84, 0xab, 0x34, 0xb0, 0xb3, 0x7c, 0xa0, 0xc0, 0x2e, 0x4a, + 0x8f, 0xaf, 0xa0, 0x01, 0x06, 0xaa, 0x94, 0xb9, 0x96, 0x8f, 0x6c, 0x6f, 0x15, 0x07, 0x5d, 0x80, + 0xd5, 0x4d, 0x43, 0x0b, 0xd0, 0x54, 0xcd, 0xd8, 0xdf, 0x73, 0xb6, 0xeb, 0x51, 0xe6, 0x5c, 0xc9, + 0x41, 0xcd, 0x78, 0x81, 0x35, 0x8e, 0x12, 0xd5, 0x4c, 0x2e, 0x84, 0x5f, 0x44, 0x88, 0x41, 0x4a, + 0x86, 0xe1, 0x70, 0x4d, 0x7f, 0x6c, 0x7b, 0xab, 0x78, 0x86, 0xb3, 0xd0, 0x0d, 0x43, 0x1c, 0x26, + 0x02, 0x31, 0x6e, 0xa1, 0x61, 0xf6, 0x6b, 0x5e, 0x5f, 0x21, 0x4d, 0xa6, 0xe6, 0x43, 0xd7, 0x2f, + 0xfb, 0xd2, 0x94, 0xa5, 0x33, 0x29, 0x92, 0xce, 0x58, 0x9e, 0xb3, 0x39, 0x55, 0xe4, 0x33, 0xe3, + 0x39, 0x5e, 0x55, 0x13, 0x70, 0xe2, 0x98, 0x14, 0xcb, 0xd0, 0x09, 0x73, 0xd6, 0x76, 0x36, 0x74, + 0xc7, 0x20, 0xc6, 0xd4, 0xa6, 0x38, 0x61, 0xde, 0xf7, 0xc1, 0xf5, 0x15, 0x51, 0x07, 0x44, 0x72, + 0x3c, 0x8d, 0x46, 0x18, 0xb7, 0x5a, 0x67, 0x05, 0xfa, 0xbe, 0x3f, 0x26, 0x2d, 0xb7, 0xb3, 0x12, + 0xed, 0x6f, 0xb9, 0x0c, 0x1d, 0x93, 0x0c, 0x70, 0x97, 0x38, 0x74, 0x36, 0x05, 0xf5, 0xe7, 0x63, + 0x92, 0x33, 0x79, 0xc0, 0x30, 0x71, 0x1e, 0xbc, 0xc8, 0xc4, 0xab, 0x68, 0x3c, 0x26, 0x0a, 0x5c, + 0x40, 0xb9, 0x75, 0xb2, 0xc9, 0xd4, 0x45, 0xa3, 0x7f, 0xe2, 0xd3, 0xa8, 0xf7, 0x81, 0xde, 0xec, + 0xf0, 0xb5, 0x4c, 0x63, 0x3f, 0x5e, 0xca, 0x7e, 0x3c, 0x43, 0xa7, 0x7e, 0x3c, 0x6d, 0x5b, 0x16, + 0x69, 0x78, 0xe2, 0xec, 0xff, 0x3c, 0x1a, 0x9c, 0xb7, 0x1b, 0x7a, 0x13, 0xfa, 0x91, 0xe9, 0x9d, + 0xb2, 0xbd, 0x55, 0x3c, 0x4d, 0x3b, 0x70, 0xb2, 0x49, 0x31, 0x42, 0x9b, 0x42, 0x52, 0xaa, 0x00, + 0x1a, 0x69, 0xd9, 0x1e, 0x81, 0x82, 0xd9, 0x50, 0x01, 0xa0, 0xa0, 0x03, 0x28, 0x51, 0x01, 0x42, + 0x62, 0x7c, 0x0d, 0x0d, 0x54, 0xe9, 0x82, 0xd7, 0xb0, 0x9b, 0x5c, 0xf9, 0x60, 0x4e, 0x86, 0x45, + 0x50, 0x1c, 0x34, 0x3e, 0x91, 0x3a, 0x87, 0x46, 0xa7, 0x9b, 0x26, 0xb1, 0x3c, 0xb1, 0xd5, 0x74, + 0x48, 0x95, 0x56, 0x89, 0xe5, 0x89, 0xad, 0x86, 0xc1, 0xa7, 0x53, 0xa8, 0xd8, 0xea, 0x80, 0x54, + 0xfd, 0xd7, 0x39, 0xf4, 0xd8, 0xed, 0xce, 0x0a, 0x71, 0x2c, 0xe2, 0x11, 0x97, 0xaf, 0x8c, 0x01, + 0xd7, 0x45, 0x34, 0x1e, 0x43, 0x72, 0xee, 0xb0, 0x62, 0xad, 0x07, 0xc8, 0x3a, 0x5f, 0x6c, 0xc5, + 0x69, 0x2f, 0x56, 0x14, 0xcf, 0xa1, 0xb1, 0x10, 0x48, 0x1b, 0xe1, 0x2a, 0x59, 0x98, 0xd3, 0x2f, + 0x6e, 0x6f, 0x15, 0x27, 0x04, 0x6e, 0xb4, 0xd9, 0xa2, 0x06, 0x47, 0x8b, 0xe1, 0xdb, 0xa8, 0x10, + 0x82, 0x6e, 0x39, 0x76, 0xa7, 0xed, 0x2a, 0x39, 0x60, 0x55, 0xdc, 0xde, 0x2a, 0x3e, 0x2e, 0xb0, + 0x5a, 0x05, 0xa4, 0xb8, 0x92, 0x46, 0x0b, 0xe2, 0xef, 0xcd, 0x88, 0xdc, 0xf8, 0x28, 0xcc, 0xc3, + 0x28, 0x7c, 0xc1, 0x1f, 0x85, 0xa9, 0x42, 0x9a, 0x8c, 0x96, 0xe4, 0x83, 0x32, 0xd2, 0x8c, 0xd8, + 0xa0, 0x8c, 0xd5, 0x38, 0x31, 0x8d, 0xce, 0x24, 0xf2, 0xda, 0x93, 0x56, 0xff, 0x71, 0x4e, 0xe4, + 0x52, 0xb5, 0x8d, 0xa0, 0x33, 0x97, 0xc4, 0xce, 0xac, 0xda, 0x06, 0x6c, 0x97, 0x32, 0xe1, 0x22, + 0x26, 0x34, 0xb6, 0x6d, 0x1b, 0xd1, 0x5d, 0x53, 0xbc, 0x2c, 0x7e, 0x1b, 0x9d, 0x8d, 0x01, 0xd9, + 0x74, 0xcd, 0xb4, 0xff, 0xd2, 0xf6, 0x56, 0x51, 0x4d, 0xe0, 0x1a, 0x9d, 0xbd, 0x53, 0xb8, 0x60, + 0x1d, 0x9d, 0x13, 0xa4, 0x6e, 0x5b, 0x9e, 0x6e, 0x5a, 0x7c, 0x97, 0xc7, 0x46, 0xc9, 0xd3, 0xdb, + 0x5b, 0xc5, 0xa7, 0x44, 0x1d, 0xf4, 0x69, 0xa2, 0x8d, 0x4f, 0xe3, 0x83, 0x0d, 0xa4, 0x24, 0xa0, + 0x2a, 0x2d, 0x7d, 0xd5, 0xdf, 0xba, 0x5e, 0xde, 0xde, 0x2a, 0x7e, 0x28, 0xb1, 0x0e, 0x93, 0x52, + 0x89, 0x4b, 0x65, 0x1a, 0x27, 0xac, 0x21, 0x1c, 0xe2, 0x16, 0x6d, 0x83, 0xc0, 0x37, 0xf4, 0x02, + 0x7f, 0x75, 0x7b, 0xab, 0x78, 0x51, 0xe0, 0x6f, 0xd9, 0x06, 0x89, 0x36, 0x3f, 0xa1, 0xb4, 0xfa, + 0xab, 0x39, 0x74, 0xb1, 0x56, 0x5a, 0x98, 0xaf, 0x18, 0xfe, 0xde, 0xa2, 0xea, 0xd8, 0x0f, 0x4c, + 0x43, 0x18, 0xbd, 0x2b, 0xe8, 0x5c, 0x04, 0x35, 0x03, 0xdb, 0x99, 0x60, 0x57, 0x0b, 0xdf, 0xe6, + 0xef, 0x5b, 0xda, 0x9c, 0xa6, 0xce, 0xf6, 0x3c, 0x75, 0x69, 0x4b, 0x9f, 0xc6, 0x88, 0xf6, 0x51, + 0x04, 0x55, 0x5b, 0xb3, 0x1d, 0xaf, 0xd1, 0xf1, 0xb8, 0x12, 0x40, 0x1f, 0xc5, 0xea, 0x70, 0x39, + 0x51, 0x97, 0x2a, 0x7c, 0x3e, 0xf8, 0x87, 0x32, 0xa8, 0x50, 0xf2, 0x3c, 0xc7, 0x5c, 0xe9, 0x78, + 0x64, 0x41, 0x6f, 0xb7, 0x4d, 0x6b, 0x15, 0xc6, 0xfa, 0xd0, 0xf5, 0x97, 0x83, 0x35, 0xb2, 0xab, + 0x24, 0x26, 0xa3, 0xc5, 0x85, 0x21, 0xaa, 0xfb, 0xa8, 0x7a, 0x8b, 0xe1, 0xc4, 0x21, 0x1a, 0x2d, + 0x47, 0x87, 0x68, 0x22, 0xaf, 0x3d, 0x0d, 0xd1, 0x1f, 0xce, 0xa1, 0xf3, 0x4b, 0xeb, 0x9e, 0xae, + 0x11, 0xd7, 0xee, 0x38, 0x0d, 0xe2, 0xde, 0x69, 0x1b, 0xba, 0x47, 0xc2, 0x91, 0x5a, 0x44, 0xbd, + 0x25, 0xc3, 0x20, 0x06, 0xb0, 0xeb, 0x65, 0xe7, 0x2f, 0x9d, 0x02, 0x34, 0x06, 0xc7, 0x1f, 0x46, + 0xfd, 0xbc, 0x0c, 0x70, 0xef, 0x9d, 0x1a, 0xda, 0xde, 0x2a, 0xf6, 0x77, 0x18, 0x48, 0xf3, 0x71, + 0x94, 0xac, 0x4c, 0x9a, 0x84, 0x92, 0xe5, 0x42, 0x32, 0x83, 0x81, 0x34, 0x1f, 0x87, 0xdf, 0x40, + 0xa3, 0xc0, 0x36, 0x68, 0x0f, 0x9f, 0xfb, 0x4e, 0xfb, 0xd2, 0x15, 0x1b, 0xcb, 0x96, 0x26, 0x68, + 0x4d, 0xdd, 0xf1, 0x0b, 0x68, 0x11, 0x06, 0xf8, 0x1e, 0x2a, 0xf0, 0x46, 0x84, 0x4c, 0x7b, 0xbb, + 0x30, 0x3d, 0xb3, 0xbd, 0x55, 0x1c, 0xe7, 0xed, 0x17, 0xd8, 0xc6, 0x98, 0x50, 0xc6, 0xbc, 0xd9, + 0x21, 0xe3, 0xbe, 0x9d, 0x18, 0xf3, 0x2f, 0x16, 0x19, 0x47, 0x99, 0xa8, 0x6f, 0xa1, 0x61, 0xb1, + 0x20, 0x3e, 0x0b, 0x67, 0x5c, 0x36, 0x4e, 0xe0, 0x74, 0x6c, 0x1a, 0x70, 0xb0, 0x7d, 0x0e, 0x0d, + 0x95, 0x89, 0xdb, 0x70, 0xcc, 0x36, 0xdd, 0x35, 0x70, 0x25, 0x1f, 0xdb, 0xde, 0x2a, 0x0e, 0x19, + 0x21, 0x58, 0x13, 0x69, 0xd4, 0xff, 0x99, 0x41, 0x67, 0x29, 0xef, 0x92, 0xeb, 0x9a, 0xab, 0x56, + 0x4b, 0x5c, 0xb6, 0xaf, 0xa2, 0xbe, 0x1a, 0xd4, 0xc7, 0x6b, 0x3a, 0xbd, 0xbd, 0x55, 0x2c, 0xb0, + 0x16, 0x08, 0x7a, 0xc8, 0x69, 0x82, 0x03, 0x5e, 0x76, 0x87, 0x03, 0x1e, 0xdd, 0xd2, 0x7a, 0xba, + 0xe3, 0x99, 0xd6, 0x6a, 0xcd, 0xd3, 0xbd, 0x8e, 0x2b, 0x6d, 0x69, 0x39, 0xa6, 0xee, 0x02, 0x4a, + 0xda, 0xd2, 0x4a, 0x85, 0xf0, 0xab, 0x68, 0x78, 0xc6, 0x32, 0x42, 0x26, 0x6c, 0x42, 0x7c, 0x9c, + 0xee, 0x34, 0x09, 0xc0, 0xe3, 0x2c, 0xa4, 0x02, 0xea, 0xcf, 0x65, 0x90, 0xc2, 0x4e, 0x63, 0xf3, + 0xa6, 0xeb, 0x2d, 0x90, 0xd6, 0x8a, 0x30, 0x3b, 0xcd, 0xfa, 0xc7, 0x3b, 0x8a, 0x13, 0xd6, 0x22, + 0xd8, 0x0a, 0xf0, 0xe3, 0x5d, 0xd3, 0x74, 0xbd, 0xe8, 0x64, 0x18, 0x29, 0x85, 0x2b, 0xa8, 0x9f, + 0x71, 0x66, 0x7b, 0x89, 0xa1, 0xeb, 0x8a, 0xaf, 0x08, 0xd1, 0xaa, 0x99, 0x32, 0xb4, 0x18, 0xb1, + 0x78, 0x3e, 0xe7, 0xe5, 0xd5, 0x5f, 0xc8, 0xa2, 0x42, 0xb4, 0x10, 0xbe, 0x87, 0x06, 0x5e, 0xb7, + 0x4d, 0x8b, 0x18, 0x4b, 0x16, 0xb4, 0xb0, 0xfb, 0x2d, 0x85, 0xbf, 0x17, 0x3f, 0xf5, 0x0e, 0x94, + 0xa9, 0x8b, 0x3b, 0x58, 0xb8, 0xb4, 0x08, 0x98, 0xe1, 0x4f, 0xa1, 0x41, 0xba, 0x07, 0x7c, 0x00, + 0x9c, 0xb3, 0x3b, 0x72, 0x7e, 0x82, 0x73, 0x3e, 0xed, 0xb0, 0x42, 0x71, 0xd6, 0x21, 0x3b, 0xaa, + 0x57, 0x1a, 0xd1, 0x5d, 0xdb, 0xe2, 0x3d, 0x0f, 0x7a, 0xe5, 0x00, 0x44, 0xd4, 0x2b, 0x46, 0x43, + 0xb7, 0xae, 0xec, 0x63, 0xa1, 0x1b, 0x84, 0xb3, 0x0b, 0x93, 0x55, 0xb4, 0x07, 0x04, 0x62, 0xf5, + 0xfb, 0xb3, 0xe8, 0xd9, 0x50, 0x64, 0x1a, 0x79, 0x60, 0x92, 0x0d, 0x2e, 0xce, 0x35, 0xb3, 0xcd, + 0x0f, 0x8f, 0x54, 0xe5, 0xdd, 0xe9, 0x35, 0xdd, 0x5a, 0x25, 0x06, 0xbe, 0x82, 0x7a, 0xe9, 0x09, + 0xdf, 0x55, 0x32, 0xb0, 0x5d, 0x83, 0xe9, 0xc4, 0xa1, 0x00, 0xf1, 0xf6, 0x01, 0x28, 0xb0, 0x8d, + 0xfa, 0x96, 0x1d, 0xdd, 0xf4, 0xfc, 0x9e, 0x2d, 0xc5, 0x7b, 0x76, 0x17, 0x35, 0x4e, 0x32, 0x1e, + 0x6c, 0xce, 0x07, 0x41, 0x78, 0x00, 0x10, 0x05, 0xc1, 0x48, 0x26, 0x5e, 0x44, 0x43, 0x02, 0xf1, + 0x9e, 0x26, 0xf5, 0xaf, 0xe6, 0x45, 0x5d, 0xf7, 0x9b, 0xc5, 0x75, 0xfd, 0x1a, 0xd5, 0x51, 0xd7, + 0xa5, 0xbb, 0x0a, 0xa6, 0xe4, 0x5c, 0x13, 0x01, 0x24, 0x6b, 0x22, 0x80, 0xf0, 0x0d, 0x34, 0xc0, + 0x58, 0x04, 0xe7, 0x57, 0x38, 0xfb, 0x3a, 0x00, 0x93, 0x97, 0xe6, 0x80, 0x10, 0xff, 0x4c, 0x06, + 0x5d, 0xe8, 0x2a, 0x09, 0x50, 0x86, 0xa1, 0xeb, 0x1f, 0xdb, 0x97, 0x18, 0xa7, 0x9e, 0xdd, 0xde, + 0x2a, 0x5e, 0x69, 0x05, 0x24, 0x75, 0x47, 0xa0, 0xa9, 0x37, 0x18, 0x91, 0xd0, 0xae, 0xee, 0x4d, + 0xa1, 0x9b, 0x47, 0x56, 0xe9, 0x2c, 0xdc, 0xe1, 0x58, 0x8d, 0x4d, 0xbf, 0x91, 0xf9, 0x70, 0xf3, + 0xc8, 0xbf, 0xf7, 0xbe, 0x4f, 0x92, 0x50, 0x4d, 0x0a, 0x17, 0xdc, 0x40, 0xe7, 0x18, 0xa6, 0xac, + 0x6f, 0x2e, 0xdd, 0x5f, 0xb0, 0x2d, 0x6f, 0xcd, 0xaf, 0xa0, 0x57, 0xbc, 0x04, 0x81, 0x0a, 0x0c, + 0x7d, 0xb3, 0x6e, 0xdf, 0xaf, 0xb7, 0x28, 0x55, 0x42, 0x1d, 0x69, 0x9c, 0xe8, 0x44, 0xcb, 0xc7, + 0x9c, 0x3f, 0x05, 0xf5, 0x85, 0x57, 0x54, 0xfe, 0x38, 0x8d, 0x4f, 0x38, 0x91, 0x42, 0x6a, 0x05, + 0x0d, 0xcf, 0xdb, 0x8d, 0xf5, 0x40, 0x5d, 0x5e, 0x44, 0x7d, 0xcb, 0xba, 0xb3, 0x4a, 0x3c, 0x90, + 0xc5, 0xd0, 0xf5, 0xf1, 0x49, 0x76, 0xed, 0x4b, 0x89, 0x18, 0x62, 0x6a, 0x94, 0xcf, 0x06, 0x7d, + 0x1e, 0xfc, 0xd6, 0x78, 0x01, 0xf5, 0x1b, 0xbd, 0x68, 0x98, 0x5f, 0x51, 0xc2, 0x6c, 0x8e, 0x5f, + 0x0a, 0x2f, 0x7d, 0xf9, 0xf4, 0x15, 0x5c, 0xd3, 0x04, 0xd7, 0x4b, 0xc3, 0x94, 0xd9, 0xef, 0x6e, + 0x15, 0x33, 0xdb, 0x5b, 0xc5, 0x1e, 0x6d, 0x40, 0x38, 0x54, 0x86, 0xeb, 0x8d, 0xb0, 0xc0, 0x8a, + 0x97, 0x8e, 0x91, 0xb2, 0x6c, 0xfd, 0x79, 0x15, 0xf5, 0xf3, 0x36, 0x70, 0x8d, 0x3b, 0x17, 0xde, + 0x65, 0x48, 0x57, 0xad, 0x91, 0xd2, 0x7e, 0x29, 0xfc, 0x32, 0xea, 0x63, 0x67, 0x7b, 0x2e, 0x80, + 0xb3, 0xc9, 0x77, 0x21, 0x91, 0xe2, 0xbc, 0x0c, 0x9e, 0x43, 0x28, 0x3c, 0xd7, 0x07, 0x37, 0xcb, + 0x9c, 0x43, 0xfc, 0xc4, 0x1f, 0xe1, 0x22, 0x94, 0xc5, 0xcf, 0xa3, 0xe1, 0x65, 0xe2, 0xb4, 0x4c, + 0x4b, 0x6f, 0xd6, 0xcc, 0x77, 0xfd, 0xcb, 0x65, 0x58, 0x78, 0x5d, 0xf3, 0x5d, 0x71, 0xe4, 0x4a, + 0x74, 0xf8, 0xb3, 0x49, 0xe7, 0xe6, 0x7e, 0x68, 0xc8, 0x93, 0x3b, 0x1e, 0x28, 0x23, 0xed, 0x49, + 0x38, 0x46, 0xbf, 0x81, 0x46, 0xa4, 0x23, 0x13, 0xbf, 0x3d, 0xbc, 0x10, 0x67, 0x2d, 0x9c, 0xff, + 0x22, 0x6c, 0x65, 0x0e, 0x54, 0x93, 0x2b, 0x96, 0xe9, 0x99, 0x7a, 0x73, 0xda, 0x6e, 0xb5, 0x74, + 0xcb, 0x50, 0x06, 0x43, 0x4d, 0x36, 0x19, 0xa6, 0xde, 0x60, 0x28, 0x51, 0x93, 0xe5, 0x42, 0xf4, + 0x58, 0xce, 0xfb, 0x50, 0x23, 0x0d, 0xdb, 0xa1, 0x7b, 0x01, 0xb8, 0x1c, 0xe4, 0xc7, 0x72, 0x97, + 0xe1, 0xea, 0x8e, 0x8f, 0x14, 0x37, 0xdb, 0xd1, 0x82, 0xaf, 0xe7, 0x07, 0x86, 0x0a, 0xc3, 0xd1, + 0xfb, 0x5c, 0xf5, 0x1f, 0xe4, 0xd0, 0x10, 0x27, 0xa5, 0x4b, 0xe9, 0x89, 0x82, 0x1f, 0x44, 0xc1, + 0x13, 0x15, 0xb5, 0xef, 0xb0, 0x14, 0x55, 0xfd, 0x42, 0x36, 0x98, 0x8d, 0xaa, 0x8e, 0x69, 0x1d, + 0x6c, 0x36, 0xba, 0x84, 0xd0, 0xf4, 0x5a, 0xc7, 0x5a, 0x67, 0xef, 0x56, 0xd9, 0xf0, 0xdd, 0xaa, + 0x61, 0x6a, 0x02, 0x06, 0x5f, 0x40, 0xf9, 0x32, 0xe5, 0x4f, 0x7b, 0x66, 0x78, 0x6a, 0xf0, 0x3d, + 0xc6, 0x29, 0xf3, 0xac, 0x06, 0x60, 0x7a, 0xb8, 0x9a, 0xda, 0xf4, 0x08, 0xdb, 0xce, 0xe6, 0xd8, + 0xe1, 0x6a, 0x85, 0x02, 0x34, 0x06, 0xc7, 0x37, 0xd1, 0x78, 0x99, 0x34, 0xf5, 0xcd, 0x05, 0xb3, + 0xd9, 0x34, 0x5d, 0xd2, 0xb0, 0x2d, 0xc3, 0x05, 0x21, 0xf3, 0xea, 0x5a, 0xae, 0x16, 0x27, 0xc0, + 0x2a, 0xea, 0x5b, 0xba, 0x7f, 0xdf, 0x25, 0x1e, 0x88, 0x2f, 0x37, 0x85, 0xe8, 0xe4, 0x6c, 0x03, + 0x44, 0xe3, 0x18, 0xf5, 0x2b, 0x19, 0x7a, 0x7a, 0x71, 0xd7, 0x3d, 0xbb, 0x1d, 0x68, 0xf9, 0x81, + 0x44, 0x72, 0x25, 0xdc, 0x57, 0x64, 0xe1, 0x6b, 0xc7, 0xf8, 0xd7, 0xf6, 0xf3, 0xbd, 0x45, 0xb8, + 0xa3, 0x48, 0xfc, 0xaa, 0xdc, 0x0e, 0x5f, 0xa5, 0xfe, 0x49, 0x16, 0x9d, 0xe3, 0x2d, 0x9e, 0x6e, + 0x9a, 0xed, 0x15, 0x5b, 0x77, 0x0c, 0x8d, 0x34, 0x88, 0xf9, 0x80, 0x1c, 0xcf, 0x81, 0x27, 0x0f, + 0x9d, 0xfc, 0x01, 0x86, 0xce, 0x75, 0x38, 0x08, 0x52, 0xc9, 0xc0, 0x85, 0x2f, 0xdb, 0x54, 0x14, + 0xb6, 0xb7, 0x8a, 0xc3, 0x06, 0x03, 0xc3, 0x95, 0xbf, 0x26, 0x12, 0x51, 0x25, 0x99, 0x27, 0xd6, + 0xaa, 0xb7, 0x06, 0x4a, 0xd2, 0xcb, 0x94, 0xa4, 0x09, 0x10, 0x8d, 0x63, 0xd4, 0xff, 0x96, 0x45, + 0xa7, 0xa3, 0x22, 0xaf, 0x11, 0xcb, 0x38, 0x91, 0xf7, 0xfb, 0x23, 0xef, 0x6f, 0xe5, 0xd0, 0xe3, + 0xbc, 0x4c, 0x6d, 0x4d, 0x77, 0x88, 0x51, 0x36, 0x1d, 0xd2, 0xf0, 0x6c, 0x67, 0xf3, 0x18, 0x6f, + 0xa0, 0x0e, 0x4f, 0xec, 0x37, 0x51, 0x1f, 0x3f, 0xfe, 0xb3, 0x75, 0x66, 0x34, 0x68, 0x09, 0x40, + 0x63, 0x2b, 0x14, 0xbb, 0x3a, 0x88, 0x74, 0x56, 0xdf, 0x6e, 0x3a, 0xeb, 0xe3, 0x68, 0x24, 0x10, + 0x3d, 0x1c, 0x44, 0xfb, 0xc3, 0xdd, 0x96, 0xe1, 0x23, 0xe0, 0x2c, 0xaa, 0xc9, 0x84, 0x50, 0x9b, + 0x0f, 0xa8, 0x94, 0x61, 0x37, 0x34, 0xc2, 0x6b, 0x0b, 0xca, 0x99, 0x86, 0x26, 0x12, 0xa9, 0x5b, + 0x79, 0x34, 0x91, 0xdc, 0xed, 0x1a, 0xd1, 0x8d, 0x93, 0x5e, 0xff, 0x8e, 0xec, 0x75, 0xfc, 0x24, + 0xca, 0x57, 0x75, 0x6f, 0x8d, 0xbf, 0x83, 0xc3, 0x9b, 0xf0, 0x7d, 0xb3, 0x49, 0xea, 0x6d, 0xdd, + 0x5b, 0xd3, 0x00, 0x25, 0xcc, 0x19, 0x08, 0x38, 0x26, 0xcc, 0x19, 0xc2, 0x62, 0x3f, 0xf4, 0x44, + 0xe6, 0x72, 0x3e, 0x71, 0xb1, 0xff, 0x46, 0x3e, 0x6d, 0x5e, 0xb9, 0xe7, 0x98, 0x1e, 0x39, 0xd1, + 0xb0, 0x13, 0x0d, 0x3b, 0xa0, 0x86, 0xfd, 0x7e, 0x16, 0x8d, 0x04, 0x87, 0xa6, 0x77, 0x48, 0xe3, + 0x68, 0xd6, 0xaa, 0xf0, 0x28, 0x93, 0x3b, 0xf0, 0x51, 0xe6, 0x20, 0x0a, 0xa5, 0x06, 0x57, 0x9e, + 0x6c, 0x6b, 0x00, 0x12, 0x63, 0x57, 0x9e, 0xc1, 0x45, 0xe7, 0x93, 0xa8, 0x7f, 0x41, 0x7f, 0x68, + 0xb6, 0x3a, 0x2d, 0xbe, 0x4b, 0x07, 0xbb, 0xae, 0x96, 0xfe, 0x50, 0xf3, 0xe1, 0xea, 0xbf, 0xcd, + 0xa0, 0x51, 0x2e, 0x54, 0xce, 0xfc, 0x40, 0x52, 0x0d, 0xa5, 0x93, 0x3d, 0xb0, 0x74, 0x72, 0xfb, + 0x97, 0x8e, 0xfa, 0x77, 0x72, 0x48, 0x99, 0x35, 0x9b, 0x64, 0xd9, 0xd1, 0x2d, 0xf7, 0x3e, 0x71, + 0xf8, 0x71, 0x7a, 0x86, 0xb2, 0x3a, 0xd0, 0x07, 0x0a, 0x53, 0x4a, 0x76, 0x5f, 0x53, 0xca, 0x47, + 0xd0, 0x20, 0x6f, 0x4c, 0x60, 0x53, 0x08, 0xa3, 0xc6, 0xf1, 0x81, 0x5a, 0x88, 0xa7, 0xc4, 0xa5, + 0x76, 0xdb, 0xb1, 0x1f, 0x10, 0x87, 0xbd, 0x52, 0x71, 0x62, 0xdd, 0x07, 0x6a, 0x21, 0x5e, 0xe0, + 0x4c, 0xfc, 0xfd, 0xa2, 0xc8, 0x99, 0x38, 0x5a, 0x88, 0xc7, 0x97, 0xd1, 0xc0, 0xbc, 0xdd, 0xd0, + 0x41, 0xd0, 0x6c, 0x5a, 0x19, 0xde, 0xde, 0x2a, 0x0e, 0x34, 0x39, 0x4c, 0x0b, 0xb0, 0x94, 0xb2, + 0x6c, 0x6f, 0x58, 0x4d, 0x5b, 0x67, 0xc6, 0x2f, 0x03, 0x8c, 0xd2, 0xe0, 0x30, 0x2d, 0xc0, 0x52, + 0x4a, 0x2a, 0x73, 0x30, 0x2a, 0x1a, 0x08, 0x79, 0xde, 0xe7, 0x30, 0x2d, 0xc0, 0xaa, 0x5f, 0xc9, + 0x53, 0xed, 0x75, 0xcd, 0x77, 0x1f, 0xf9, 0x75, 0x21, 0x1c, 0x30, 0xbd, 0xfb, 0x18, 0x30, 0x8f, + 0xcc, 0x85, 0x9d, 0xfa, 0xa7, 0xfd, 0x08, 0x71, 0xe9, 0xcf, 0x9c, 0x1c, 0x0e, 0x0f, 0xa6, 0x35, + 0x65, 0x34, 0x3e, 0x63, 0xad, 0xe9, 0x56, 0x83, 0x18, 0xe1, 0xb5, 0x65, 0x1f, 0x0c, 0x6d, 0xb0, + 0xe9, 0x25, 0x1c, 0x19, 0xde, 0x5b, 0x6a, 0xf1, 0x02, 0xf8, 0x39, 0x34, 0x54, 0xb1, 0x3c, 0xe2, + 0xe8, 0x0d, 0xcf, 0x7c, 0x40, 0xf8, 0xd4, 0x00, 0x2f, 0xc3, 0x66, 0x08, 0xd6, 0x44, 0x1a, 0x7c, + 0x13, 0x0d, 0x57, 0x75, 0xc7, 0x33, 0x1b, 0x66, 0x5b, 0xb7, 0x3c, 0x57, 0x19, 0x80, 0x19, 0x0d, + 0x76, 0x18, 0x6d, 0x01, 0xae, 0x49, 0x54, 0xf8, 0xb3, 0x68, 0x10, 0x8e, 0xa6, 0x60, 0x38, 0x3d, + 0xb8, 0xe3, 0xc3, 0xe1, 0x53, 0xa1, 0x79, 0x20, 0xbb, 0x7d, 0x85, 0x17, 0xe0, 0xe8, 0xdb, 0x61, + 0xc0, 0x11, 0xbf, 0x89, 0xfa, 0x67, 0x2c, 0x03, 0x98, 0xa3, 0x1d, 0x99, 0xab, 0x9c, 0xf9, 0xd9, + 0x90, 0xb9, 0xdd, 0x8e, 0xf0, 0xf6, 0xd9, 0x25, 0x8f, 0xb2, 0xa1, 0xf7, 0x6f, 0x94, 0x0d, 0xbf, + 0x0f, 0xd7, 0xe2, 0x23, 0x87, 0x75, 0x2d, 0x3e, 0xba, 0xcf, 0x6b, 0x71, 0xf5, 0x5d, 0x34, 0x34, + 0x55, 0x9d, 0x0d, 0x46, 0xef, 0x63, 0x28, 0x57, 0xe5, 0x96, 0x0a, 0x79, 0xb6, 0x9f, 0x69, 0x9b, + 0x86, 0x46, 0x61, 0xf8, 0x0a, 0x1a, 0x98, 0x06, 0xf3, 0x37, 0xfe, 0x8a, 0x98, 0x67, 0xeb, 0x5f, + 0x03, 0x60, 0x60, 0x05, 0xeb, 0xa3, 0xf1, 0x87, 0x51, 0x7f, 0xd5, 0xb1, 0x57, 0x1d, 0xbd, 0xc5, + 0xd7, 0x60, 0x30, 0x15, 0x69, 0x33, 0x90, 0xe6, 0xe3, 0xd4, 0xbf, 0x91, 0xf1, 0xb7, 0xed, 0xb4, + 0x44, 0xad, 0x03, 0x57, 0xf3, 0x50, 0xf7, 0x00, 0x2b, 0xe1, 0x32, 0x90, 0xe6, 0xe3, 0xf0, 0x15, + 0xd4, 0x3b, 0xe3, 0x38, 0xb6, 0x23, 0x1a, 0x9b, 0x13, 0x0a, 0x10, 0x9f, 0x7b, 0x81, 0x02, 0xbf, + 0x80, 0x86, 0xd8, 0x9c, 0xc3, 0x6e, 0x34, 0x73, 0xdd, 0x5e, 0x4a, 0x45, 0x4a, 0xf5, 0x6b, 0x39, + 0x61, 0xcf, 0xc6, 0x24, 0xfe, 0x08, 0xbe, 0x0a, 0xdc, 0x40, 0xb9, 0xa9, 0xea, 0x2c, 0x9f, 0x00, + 0x4f, 0xf9, 0x45, 0x05, 0x55, 0x89, 0x94, 0xa3, 0xd4, 0xf8, 0x3c, 0xca, 0x57, 0xa9, 0xfa, 0xf4, + 0x81, 0x7a, 0x0c, 0x6c, 0x6f, 0x15, 0xf3, 0x6d, 0xaa, 0x3f, 0x00, 0x05, 0x2c, 0x3d, 0xcc, 0xb0, + 0x13, 0x13, 0xc3, 0x86, 0xe7, 0x98, 0xf3, 0x28, 0x5f, 0x72, 0x56, 0x1f, 0xf0, 0x59, 0x0b, 0xb0, + 0xba, 0xb3, 0xfa, 0x40, 0x03, 0x28, 0xbe, 0x86, 0x90, 0x46, 0xbc, 0x8e, 0x63, 0x81, 0x1f, 0xc8, + 0x20, 0xdc, 0xbf, 0xc1, 0x6c, 0xe8, 0x00, 0xb4, 0xde, 0xb0, 0x0d, 0xa2, 0x09, 0x24, 0xea, 0x4f, + 0x85, 0x0f, 0x3b, 0x65, 0xd3, 0x5d, 0x3f, 0xe9, 0xc2, 0x3d, 0x74, 0xa1, 0xce, 0xaf, 0x38, 0xe3, + 0x9d, 0x54, 0x44, 0xbd, 0xb3, 0x4d, 0x7d, 0xd5, 0x85, 0x3e, 0xe4, 0xb6, 0x64, 0xf7, 0x29, 0x40, + 0x63, 0xf0, 0x48, 0x3f, 0x0d, 0xec, 0xdc, 0x4f, 0x3f, 0xd2, 0x1b, 0x8c, 0xb6, 0x45, 0xe2, 0x6d, + 0xd8, 0xce, 0x49, 0x57, 0xed, 0xb6, 0xab, 0x2e, 0xa1, 0xfe, 0x9a, 0xd3, 0x10, 0xae, 0x2e, 0xe0, + 0x3c, 0xe0, 0x3a, 0x0d, 0x76, 0x6d, 0xe1, 0x23, 0x29, 0x5d, 0xd9, 0xf5, 0x80, 0xae, 0x3f, 0xa4, + 0x33, 0x5c, 0x8f, 0xd3, 0x71, 0x24, 0xa7, 0xab, 0xda, 0x8e, 0xc7, 0x3b, 0x2e, 0xa0, 0x6b, 0xdb, + 0x8e, 0xa7, 0xf9, 0x48, 0xfc, 0x11, 0x84, 0x96, 0xa7, 0xab, 0xbe, 0xb1, 0xfd, 0x60, 0x68, 0x0b, + 0xc8, 0xad, 0xec, 0x35, 0x01, 0x8d, 0x97, 0xd1, 0xe0, 0x52, 0x9b, 0x38, 0xec, 0x28, 0xc4, 0x3c, + 0x3b, 0x9e, 0x8e, 0x88, 0x96, 0xf7, 0xfb, 0x24, 0xff, 0x3f, 0x20, 0x67, 0xeb, 0x8b, 0xed, 0xff, + 0xd4, 0x42, 0x46, 0xf8, 0x05, 0xd4, 0x57, 0x62, 0xfb, 0xbc, 0x21, 0x60, 0x19, 0x88, 0x0c, 0x8e, + 0xa0, 0x0c, 0xc5, 0xce, 0xec, 0x3a, 0xfc, 0xad, 0x71, 0x72, 0xf5, 0x0a, 0x2a, 0x44, 0xab, 0xc1, + 0x43, 0xa8, 0x7f, 0x7a, 0x69, 0x71, 0x71, 0x66, 0x7a, 0xb9, 0xd0, 0x83, 0x07, 0x50, 0xbe, 0x36, + 0xb3, 0x58, 0x2e, 0x64, 0xd4, 0x9f, 0x15, 0x66, 0x10, 0xaa, 0x5a, 0x27, 0x4f, 0xc3, 0x07, 0x7a, + 0x6f, 0x29, 0xc0, 0x7b, 0x28, 0xdc, 0x18, 0xb4, 0x4c, 0xcf, 0x23, 0x06, 0x5f, 0x25, 0xe0, 0xbd, + 0xd0, 0x7b, 0xa8, 0xc5, 0xf0, 0xf8, 0x2a, 0x1a, 0x01, 0x18, 0x7f, 0x22, 0x64, 0xe7, 0x63, 0x5e, + 0xc0, 0x79, 0xa8, 0xc9, 0x48, 0xf5, 0xeb, 0xe1, 0xeb, 0xf0, 0x3c, 0xd1, 0x8f, 0xeb, 0x8b, 0xe2, + 0x07, 0xa4, 0xbf, 0xd4, 0x3f, 0xcf, 0x33, 0x17, 0x10, 0xe6, 0xb8, 0x77, 0x14, 0xa2, 0x0c, 0xaf, + 0x74, 0x73, 0x7b, 0xb8, 0xd2, 0xbd, 0x8a, 0xfa, 0x16, 0x88, 0xb7, 0x66, 0xfb, 0x86, 0x5f, 0x60, + 0xa1, 0xd7, 0x02, 0x88, 0x68, 0xa1, 0xc7, 0x68, 0xf0, 0x3a, 0xc2, 0xbe, 0x57, 0x5e, 0x60, 0x88, + 0xed, 0x5f, 0x21, 0x9f, 0x8b, 0x9d, 0x53, 0x6a, 0xe0, 0x92, 0x0b, 0x36, 0xf6, 0xa7, 0x03, 0x43, + 0x6f, 0xc1, 0x12, 0xeb, 0xcf, 0xb6, 0x8a, 0x7d, 0x8c, 0x46, 0x4b, 0x60, 0x8b, 0xdf, 0x40, 0x83, + 0x0b, 0xb3, 0x25, 0xee, 0xa1, 0xc7, 0xac, 0x22, 0x1e, 0x0b, 0xa4, 0xe8, 0x23, 0x02, 0x91, 0x80, + 0xbf, 0x4d, 0xeb, 0xbe, 0x1e, 0x77, 0xd0, 0x0b, 0xb9, 0x50, 0x6d, 0x61, 0x9e, 0x3b, 0xfc, 0x76, + 0x21, 0xd0, 0x16, 0xd9, 0x9f, 0x27, 0x2a, 0x2b, 0x86, 0x8d, 0x68, 0xcb, 0xc0, 0x01, 0x46, 0xf7, + 0x12, 0x1a, 0x2f, 0xb5, 0xdb, 0x4d, 0x93, 0x18, 0xa0, 0x2f, 0x5a, 0xa7, 0x49, 0x5c, 0x6e, 0xf2, + 0x03, 0xce, 0x20, 0x3a, 0x43, 0xd6, 0xc1, 0x2f, 0xb4, 0xee, 0x74, 0x64, 0xfb, 0xcc, 0x78, 0x59, + 0xf5, 0xbf, 0x66, 0x50, 0xc1, 0x37, 0x9e, 0x16, 0x3d, 0x52, 0x05, 0xcb, 0x5e, 0xb8, 0x86, 0x89, + 0xd8, 0x92, 0x02, 0x1e, 0xd7, 0x50, 0xff, 0xcc, 0xc3, 0xb6, 0xe9, 0x10, 0x77, 0x17, 0x86, 0xb0, + 0x17, 0xf8, 0x91, 0x73, 0x9c, 0xb0, 0x22, 0xb1, 0xd3, 0x26, 0x03, 0x83, 0x4b, 0x14, 0x33, 0x1f, + 0x9f, 0xf2, 0xdd, 0x6c, 0x99, 0x4b, 0x14, 0x37, 0x33, 0x97, 0x7c, 0xdc, 0x42, 0x52, 0xfc, 0x14, + 0xca, 0x2d, 0x2f, 0xcf, 0x73, 0x6d, 0x04, 0xf7, 0x66, 0xcf, 0x13, 0x7d, 0xbe, 0x28, 0x56, 0xfd, + 0xc3, 0x2c, 0x42, 0x54, 0xe9, 0xa7, 0x1d, 0xa2, 0x1f, 0xd1, 0x63, 0xce, 0x14, 0x1a, 0xf0, 0x05, + 0xce, 0x07, 0x5c, 0x60, 0xf9, 0x1c, 0xed, 0x88, 0x68, 0xdd, 0x81, 0x95, 0x7b, 0xd1, 0x37, 0xc6, + 0x65, 0x77, 0xa9, 0xb0, 0x3b, 0x04, 0x63, 0x5c, 0xdf, 0x04, 0xf7, 0x23, 0x68, 0x90, 0x6b, 0x8d, + 0x2d, 0xdd, 0xa1, 0x36, 0x7c, 0xa0, 0x16, 0xe2, 0x23, 0xea, 0xd9, 0x77, 0x80, 0xc9, 0xec, 0x4b, + 0x5c, 0xbc, 0xcc, 0x4c, 0xff, 0xd8, 0x8a, 0xf7, 0xd0, 0x2e, 0xb8, 0xd4, 0xdf, 0xcf, 0x20, 0x4c, + 0x9b, 0x55, 0xd5, 0x5d, 0x77, 0xc3, 0x76, 0x0c, 0x66, 0x81, 0x7a, 0x24, 0x82, 0x39, 0xbc, 0x47, + 0x89, 0xaf, 0x0d, 0xa0, 0x53, 0x92, 0x75, 0xdf, 0x31, 0x1f, 0x4d, 0x57, 0xe4, 0xd1, 0xd4, 0xcd, + 0xb4, 0xfd, 0x43, 0xe2, 0xab, 0x47, 0xaf, 0xe4, 0x65, 0x22, 0x3c, 0x77, 0x3c, 0x8b, 0x86, 0xf9, + 0x0f, 0xba, 0x58, 0xfa, 0xd7, 0xd9, 0x30, 0x4a, 0x5d, 0x0a, 0xd0, 0x24, 0x34, 0xfe, 0x18, 0x1a, + 0xa4, 0x03, 0x66, 0x15, 0x5c, 0xf5, 0xfb, 0x43, 0xb3, 0x71, 0xc3, 0x07, 0x8a, 0x13, 0x5e, 0x40, + 0x29, 0x38, 0x0b, 0x0c, 0xec, 0xc2, 0x59, 0xe0, 0x6d, 0x34, 0x54, 0xb2, 0x2c, 0xdb, 0x83, 0x9d, + 0xb8, 0xcb, 0xef, 0x1f, 0x53, 0x97, 0xde, 0xa7, 0xc0, 0x03, 0x36, 0xa4, 0x4f, 0x5c, 0x7b, 0x45, + 0x86, 0xf8, 0xba, 0x6f, 0xfa, 0x4e, 0x1c, 0x6e, 0x3a, 0x0a, 0x77, 0xb0, 0x0e, 0x87, 0xc5, 0x2d, + 0xdf, 0xa1, 0xf3, 0x46, 0xaa, 0x8e, 0xdd, 0xb6, 0x5d, 0x62, 0x30, 0x41, 0x0d, 0x85, 0xfe, 0xc4, + 0x6d, 0x8e, 0x00, 0x67, 0x15, 0xc9, 0x6d, 0x5e, 0x2a, 0x82, 0xef, 0xa3, 0xd3, 0xfe, 0x6b, 0x50, + 0xe0, 0x16, 0x54, 0x29, 0xbb, 0xca, 0x30, 0xb8, 0x1e, 0xe0, 0xa8, 0x32, 0x54, 0xca, 0x53, 0x17, + 0xfd, 0xbb, 0x4f, 0xdf, 0xaf, 0xa8, 0x6e, 0x1a, 0x62, 0x57, 0x27, 0xf2, 0xc3, 0xdf, 0x85, 0x86, + 0x16, 0xf4, 0x87, 0xe5, 0x0e, 0x3f, 0x60, 0x8d, 0xec, 0xfe, 0x8a, 0xb5, 0xa5, 0x3f, 0xac, 0x1b, + 0xbc, 0x5c, 0x64, 0xd1, 0x13, 0x59, 0xe2, 0x3a, 0x3a, 0x5b, 0x75, 0xec, 0x96, 0xed, 0x11, 0x23, + 0xe2, 0x61, 0x33, 0x16, 0xba, 0xe4, 0xb5, 0x39, 0x45, 0xbd, 0x8b, 0xab, 0x4d, 0x0a, 0x1b, 0xdc, + 0x42, 0x63, 0x25, 0xd7, 0xed, 0xb4, 0x48, 0x78, 0x0d, 0x5d, 0xd8, 0xf1, 0x33, 0x9e, 0xe6, 0xa6, + 0x89, 0x8f, 0xeb, 0x50, 0x94, 0xdd, 0x42, 0xd7, 0x3d, 0x53, 0xac, 0x11, 0xbe, 0x25, 0xca, 0xfb, + 0xf5, 0xfc, 0xc0, 0x68, 0x61, 0x4c, 0x3b, 0x17, 0x6f, 0xcc, 0xb2, 0xe9, 0x35, 0x89, 0xfa, 0x1b, + 0x19, 0x84, 0x42, 0x01, 0xe3, 0x67, 0xe5, 0x78, 0x20, 0x99, 0xf0, 0x36, 0x93, 0xbb, 0x28, 0x4b, + 0x01, 0x40, 0xf0, 0x79, 0x94, 0x07, 0x37, 0xf6, 0x6c, 0x78, 0x7b, 0xb2, 0x6e, 0x5a, 0x86, 0x06, + 0x50, 0x8a, 0x15, 0xfc, 0x4d, 0x01, 0x0b, 0x2f, 0x77, 0x6c, 0xdb, 0x52, 0x46, 0x63, 0xb5, 0xce, + 0x8a, 0x5f, 0xb7, 0xe0, 0x3c, 0x03, 0xde, 0xf4, 0x6e, 0x67, 0x25, 0xf0, 0x38, 0x93, 0x62, 0x15, + 0xc8, 0x45, 0xd4, 0xaf, 0x64, 0x22, 0xb3, 0xe0, 0x11, 0x2e, 0x7a, 0x1f, 0x8a, 0x3f, 0xc6, 0xc6, + 0xa7, 0x25, 0xf5, 0xef, 0x66, 0xd1, 0x50, 0xd5, 0x76, 0x3c, 0x1e, 0x17, 0xe0, 0x78, 0xaf, 0x42, + 0xc2, 0xb1, 0x25, 0xbf, 0x87, 0x63, 0xcb, 0x79, 0x94, 0x17, 0xec, 0x10, 0xd9, 0xe5, 0xa7, 0x61, + 0x38, 0x1a, 0x40, 0xd5, 0xef, 0xce, 0x22, 0xf4, 0xe6, 0x73, 0xcf, 0x3d, 0xc2, 0x02, 0x52, 0xff, + 0x76, 0x06, 0x8d, 0xf1, 0xdb, 0x78, 0x21, 0xb2, 0x4e, 0xbf, 0xff, 0x8e, 0x22, 0x8e, 0x4b, 0x06, + 0xd2, 0x7c, 0x1c, 0x5d, 0x02, 0x66, 0x1e, 0x9a, 0x1e, 0x5c, 0x48, 0x0a, 0xa1, 0x75, 0x08, 0x87, + 0x89, 0x4b, 0x80, 0x4f, 0x87, 0x9f, 0xf5, 0xdf, 0x19, 0x72, 0xe1, 0xba, 0x47, 0x0b, 0xcc, 0x24, + 0xbe, 0x35, 0xa8, 0xbf, 0x9c, 0x47, 0xf9, 0x99, 0x87, 0xa4, 0x71, 0xcc, 0xbb, 0x46, 0xb8, 0xbd, + 0xc8, 0x1f, 0xf0, 0xf6, 0x62, 0x3f, 0x0f, 0xa7, 0xaf, 0x86, 0xfd, 0xd9, 0x27, 0x57, 0x1f, 0xe9, + 0xf9, 0x68, 0xf5, 0x7e, 0x4f, 0x1f, 0xbf, 0x77, 0xf7, 0x7f, 0x96, 0x43, 0xb9, 0xda, 0x74, 0xf5, + 0x44, 0x6f, 0x8e, 0x54, 0x6f, 0xba, 0x3f, 0x4c, 0xa9, 0xc1, 0x5d, 0xf3, 0x40, 0x68, 0x0a, 0x16, + 0xb9, 0x56, 0xfe, 0x56, 0x0e, 0x8d, 0xd6, 0x66, 0x97, 0xab, 0xc2, 0x75, 0xcf, 0x6d, 0x66, 0xae, + 0x03, 0x86, 0x23, 0xac, 0x4b, 0xcf, 0xc7, 0xf6, 0x33, 0x77, 0x2a, 0x96, 0xf7, 0xfc, 0xcd, 0xbb, + 0x7a, 0xb3, 0x43, 0xe0, 0x6e, 0x80, 0x19, 0xf7, 0xb9, 0xe6, 0xbb, 0xe4, 0xc7, 0xc0, 0xbb, 0xd7, + 0x67, 0x80, 0x3f, 0x81, 0x72, 0x77, 0xf8, 0xb3, 0x6b, 0x1a, 0x9f, 0x1b, 0xd7, 0x19, 0x1f, 0x3a, + 0x09, 0xe6, 0x3a, 0xa6, 0x01, 0x1c, 0x68, 0x29, 0x5a, 0xf8, 0x16, 0x5f, 0x80, 0x77, 0x55, 0x78, + 0xd5, 0x2f, 0x7c, 0xab, 0x52, 0xc6, 0x35, 0x34, 0x54, 0x25, 0x4e, 0xcb, 0x84, 0x8e, 0xf2, 0xe7, + 0xec, 0xee, 0x4c, 0xe8, 0x49, 0x65, 0xa8, 0x1d, 0x16, 0x02, 0x66, 0x22, 0x17, 0xfc, 0x16, 0x42, + 0x6c, 0x8f, 0xb2, 0xcb, 0x68, 0x6d, 0x17, 0x60, 0xdf, 0xcf, 0xb6, 0x96, 0x09, 0x7b, 0x3c, 0x81, + 0x19, 0x5e, 0x47, 0x85, 0x05, 0xdb, 0x30, 0xef, 0x9b, 0xcc, 0xbe, 0x0a, 0x2a, 0xe8, 0xdb, 0xd9, + 0xaa, 0x81, 0x6e, 0x25, 0x5b, 0x42, 0xb9, 0xa4, 0x6a, 0x62, 0x8c, 0xd5, 0x7f, 0xd2, 0x8b, 0xf2, + 0xb4, 0xdb, 0x4f, 0xc6, 0xef, 0x41, 0xc6, 0x6f, 0x09, 0x15, 0xee, 0xd9, 0xce, 0xba, 0x69, 0xad, + 0x06, 0xa6, 0xaf, 0xfc, 0x6c, 0x0a, 0xcf, 0xf5, 0x1b, 0x0c, 0x57, 0x0f, 0xac, 0x64, 0xb5, 0x18, + 0xf9, 0x0e, 0x23, 0xf8, 0x45, 0x84, 0x98, 0x43, 0x2b, 0xd0, 0x0c, 0x84, 0x1e, 0xe9, 0xcc, 0xdd, + 0x15, 0xac, 0x69, 0x45, 0x8f, 0xf4, 0x90, 0x98, 0x1e, 0xc2, 0xd9, 0x83, 0xe7, 0x20, 0x18, 0xd7, + 0xc2, 0x21, 0x1c, 0x1e, 0x3c, 0xc5, 0x4d, 0x00, 0x7b, 0xfa, 0xac, 0x22, 0x24, 0x5c, 0x22, 0xa3, + 0x88, 0x20, 0xa4, 0xc9, 0x81, 0xc7, 0x80, 0x4a, 0xb8, 0x43, 0xd6, 0x04, 0x1e, 0xf8, 0xf9, 0xc8, + 0x2b, 0x17, 0x96, 0xb8, 0xa5, 0x3e, 0x72, 0x85, 0x56, 0x12, 0xc3, 0x3b, 0x59, 0x49, 0xa8, 0x5f, + 0xc8, 0xa2, 0xc1, 0x5a, 0x67, 0xc5, 0xdd, 0x74, 0x3d, 0xd2, 0x3a, 0xe6, 0x6a, 0xec, 0x1f, 0xaf, + 0xf2, 0x89, 0xc7, 0xab, 0xa7, 0x7c, 0xa1, 0x08, 0xf7, 0x8e, 0xc1, 0x96, 0xce, 0x17, 0xc7, 0x2f, + 0x65, 0x51, 0x81, 0xdd, 0x8e, 0x97, 0x4d, 0xb7, 0x71, 0x08, 0x16, 0xbb, 0x47, 0x2f, 0x95, 0x83, + 0xbd, 0x28, 0xed, 0xc2, 0x0e, 0x5a, 0xfd, 0x7c, 0x16, 0x0d, 0x95, 0x3a, 0xde, 0x5a, 0xc9, 0x03, + 0xdd, 0x7a, 0x24, 0xcf, 0x27, 0xbf, 0x93, 0x41, 0x63, 0xb4, 0x21, 0xcb, 0xf6, 0x3a, 0xb1, 0x0e, + 0xe1, 0xe2, 0x51, 0xbc, 0x40, 0xcc, 0xee, 0xf3, 0x02, 0xd1, 0x97, 0x65, 0x6e, 0x6f, 0xb2, 0x84, + 0xeb, 0x72, 0xcd, 0x6e, 0x92, 0xe3, 0xfd, 0x19, 0x87, 0x78, 0x5d, 0xee, 0x0b, 0xe4, 0x10, 0xae, + 0x52, 0xbe, 0x33, 0x04, 0xf2, 0xa3, 0x59, 0x74, 0x9a, 0x87, 0x09, 0xe5, 0x87, 0xa3, 0x13, 0x5d, + 0x49, 0x15, 0xcd, 0x89, 0xd6, 0x70, 0xd1, 0xfc, 0x74, 0x0e, 0x9d, 0x86, 0x60, 0x6a, 0x74, 0xcf, + 0xf8, 0x1d, 0x30, 0x51, 0xe2, 0x86, 0xfc, 0x42, 0xb3, 0x90, 0xf0, 0x42, 0xf3, 0x67, 0x5b, 0xc5, + 0xe7, 0x57, 0x4d, 0x6f, 0xad, 0xb3, 0x32, 0xd9, 0xb0, 0x5b, 0xd7, 0x56, 0x1d, 0xfd, 0x81, 0xc9, + 0xde, 0x26, 0xf4, 0xe6, 0xb5, 0x20, 0xe2, 0xb6, 0xde, 0x36, 0x79, 0x2c, 0xee, 0x1a, 0x6c, 0xc4, + 0x28, 0x57, 0xff, 0x6d, 0xc7, 0x45, 0xe8, 0x75, 0xdb, 0xb4, 0xb8, 0x55, 0x03, 0x5b, 0x85, 0x6b, + 0x74, 0xf3, 0xfa, 0x8e, 0x6d, 0x5a, 0xf5, 0xa8, 0x69, 0xc3, 0x5e, 0xeb, 0x0b, 0x59, 0x6b, 0x42, + 0x35, 0xea, 0xbf, 0xc9, 0xa0, 0xc7, 0x64, 0x2d, 0xfe, 0x4e, 0x58, 0xd8, 0xfe, 0x56, 0x16, 0x9d, + 0xb9, 0x05, 0xc2, 0x09, 0x5e, 0x99, 0x4f, 0xe6, 0x2d, 0x3e, 0x38, 0x13, 0x64, 0x73, 0x32, 0x71, + 0x71, 0xd9, 0xfc, 0xab, 0x0c, 0x3a, 0xb5, 0x54, 0x29, 0x4f, 0x7f, 0x87, 0x68, 0x4d, 0xfc, 0x7b, + 0x8e, 0x77, 0x4f, 0xc3, 0xf7, 0xd4, 0x4a, 0x0b, 0xf3, 0xdf, 0x49, 0xfd, 0x23, 0x7d, 0xcf, 0x31, + 0xef, 0x9f, 0xdf, 0xee, 0x43, 0x43, 0xb7, 0x3b, 0x2b, 0x84, 0xbf, 0xf9, 0x3d, 0xd2, 0x07, 0xea, + 0xeb, 0x68, 0x88, 0x8b, 0x01, 0x2e, 0xa3, 0x84, 0xc0, 0x23, 0xdc, 0x91, 0x94, 0xf9, 0x76, 0x8b, + 0x44, 0xf8, 0x3c, 0xca, 0xdf, 0x25, 0xce, 0x8a, 0x68, 0x93, 0xff, 0x80, 0x38, 0x2b, 0x1a, 0x40, + 0xf1, 0x7c, 0x68, 0x2a, 0x57, 0xaa, 0x56, 0x20, 0x08, 0x35, 0xbf, 0x07, 0x83, 0xa8, 0xda, 0x81, + 0x39, 0x81, 0xde, 0x36, 0x59, 0xf8, 0x6a, 0xd1, 0x1f, 0x28, 0x5a, 0x12, 0x2f, 0xa2, 0x71, 0xf1, + 0x3d, 0x99, 0x45, 0x60, 0x1e, 0x48, 0x60, 0x97, 0x14, 0x7b, 0x39, 0x5e, 0x14, 0xbf, 0x8a, 0x86, + 0x7d, 0x20, 0xbc, 0x8c, 0x0f, 0x86, 0x61, 0x3f, 0x03, 0x56, 0x91, 0xf0, 0xee, 0x52, 0x01, 0x91, + 0x01, 0xdc, 0xee, 0xa0, 0x04, 0x06, 0x11, 0x4b, 0x03, 0xa9, 0x00, 0xfe, 0x18, 0x30, 0x68, 0xdb, + 0x96, 0x4b, 0xe0, 0x0d, 0x70, 0x08, 0x0c, 0xd6, 0xc1, 0x14, 0xcf, 0xe1, 0x70, 0xe6, 0x96, 0x20, + 0x91, 0xe1, 0x25, 0x84, 0xc2, 0xb7, 0x1a, 0xee, 0xfc, 0xb5, 0xe7, 0x57, 0x24, 0x81, 0x85, 0x78, + 0xcb, 0x3a, 0xb2, 0x9f, 0x5b, 0x56, 0xf5, 0xf7, 0xb2, 0x68, 0xa8, 0xd4, 0x6e, 0x07, 0x43, 0xe1, + 0x59, 0xd4, 0x57, 0x6a, 0xb7, 0xef, 0x68, 0x15, 0x31, 0x0c, 0xa4, 0xde, 0x6e, 0xd7, 0x3b, 0x8e, + 0x29, 0x9a, 0xda, 0x30, 0x22, 0x3c, 0x8d, 0x46, 0x4a, 0xed, 0x76, 0xb5, 0xb3, 0xd2, 0x34, 0x1b, + 0x42, 0x54, 0x79, 0x96, 0x00, 0xa3, 0xdd, 0xae, 0xb7, 0x01, 0x13, 0x4d, 0x2d, 0x20, 0x97, 0xc1, + 0x6f, 0x83, 0xcb, 0x34, 0x0f, 0x6a, 0xce, 0xc2, 0x26, 0xab, 0x41, 0x00, 0xc8, 0xb0, 0x6d, 0x93, + 0x01, 0x11, 0x0b, 0x94, 0x79, 0xde, 0x0f, 0x37, 0x4a, 0x2b, 0x8a, 0x05, 0x2f, 0x0f, 0x59, 0xe2, + 0x8f, 0xa2, 0xfe, 0x52, 0xbb, 0x2d, 0x5c, 0xe3, 0xc1, 0x5b, 0x2d, 0x2d, 0x15, 0xe9, 0x63, 0x9f, + 0x6c, 0xe2, 0x65, 0x34, 0x2a, 0x57, 0xb6, 0xa7, 0x40, 0x9b, 0xdf, 0xce, 0xc0, 0x07, 0x1d, 0x73, + 0x53, 0xb1, 0x1b, 0x28, 0x57, 0x6a, 0xb7, 0xf9, 0x7c, 0x74, 0x2a, 0xa1, 0x3f, 0xa2, 0xee, 0x23, + 0xa5, 0x76, 0xdb, 0xff, 0x74, 0x66, 0xaa, 0xfa, 0x68, 0x7d, 0xfa, 0xd7, 0xd8, 0xa7, 0x1f, 0x6f, + 0x7b, 0x50, 0xf5, 0x97, 0x73, 0x68, 0xac, 0xd4, 0x6e, 0x9f, 0x04, 0xe8, 0x3c, 0x2c, 0x27, 0x95, + 0xe7, 0x10, 0x12, 0xa6, 0xc7, 0xfe, 0xc0, 0x64, 0x7b, 0x48, 0x98, 0x1a, 0x95, 0x8c, 0x26, 0x10, + 0xf9, 0xea, 0x37, 0xb0, 0x27, 0xf5, 0xfb, 0x7c, 0x0e, 0xa6, 0xe2, 0xe3, 0xee, 0x70, 0xff, 0x41, + 0xe9, 0x36, 0xde, 0x07, 0x7d, 0x7b, 0xea, 0x83, 0xdf, 0x92, 0x06, 0x0f, 0x04, 0x7c, 0x3c, 0xe9, + 0x85, 0xde, 0x03, 0x6d, 0x8b, 0x47, 0x45, 0x61, 0x72, 0x2f, 0x60, 0x3f, 0x08, 0x3d, 0xf7, 0x49, + 0x6f, 0x50, 0x54, 0xdd, 0x34, 0xb4, 0x08, 0xad, 0xdf, 0x87, 0xfd, 0x7b, 0xea, 0xc3, 0xad, 0x2c, + 0xf8, 0x9d, 0x04, 0x3e, 0xed, 0x07, 0x3f, 0x5d, 0x5c, 0x43, 0x88, 0x3d, 0xe8, 0x04, 0xd6, 0x62, + 0x23, 0xcc, 0x7d, 0x95, 0xc5, 0xa6, 0xe7, 0xee, 0xab, 0x21, 0x49, 0xf0, 0xf0, 0x9c, 0x4b, 0x7c, + 0x78, 0xbe, 0x82, 0x06, 0x34, 0x7d, 0xe3, 0x8d, 0x0e, 0x71, 0x36, 0xf9, 0x76, 0x86, 0x85, 0x8c, + 0xd1, 0x37, 0xea, 0x9f, 0xa3, 0x40, 0x2d, 0x40, 0x63, 0x35, 0x70, 0x5c, 0x12, 0x1e, 0xda, 0xd8, + 0xed, 0x5e, 0xe0, 0xae, 0xb4, 0x1f, 0x45, 0xc7, 0x2f, 0xa1, 0x5c, 0xe9, 0x5e, 0x8d, 0x4b, 0x36, + 0xe8, 0xda, 0xd2, 0xbd, 0x1a, 0x97, 0x57, 0x6a, 0xd9, 0x7b, 0x35, 0xf5, 0xf3, 0x59, 0x84, 0xe3, + 0x94, 0xf8, 0x79, 0x34, 0x08, 0xd0, 0x55, 0xaa, 0x33, 0x62, 0x52, 0xa3, 0x0d, 0xb7, 0xee, 0x00, + 0x54, 0xda, 0xdc, 0xf9, 0xa4, 0xf8, 0x45, 0xc8, 0xdf, 0xc6, 0xd3, 0x6a, 0x48, 0x49, 0x8d, 0x36, + 0x5c, 0x3f, 0xe3, 0x59, 0x24, 0x7d, 0x1b, 0x27, 0x86, 0x7d, 0xe1, 0xbd, 0xda, 0x9c, 0xed, 0x7a, + 0x5c, 0xd4, 0x6c, 0x5f, 0xb8, 0xe1, 0x42, 0x36, 0x2d, 0x69, 0x5f, 0xc8, 0xc8, 0x20, 0x23, 0xc0, + 0xbd, 0x1a, 0xb3, 0xfe, 0x35, 0x34, 0xbb, 0xe9, 0x6f, 0x28, 0x59, 0x46, 0x80, 0x0d, 0xb7, 0xce, + 0x2c, 0x87, 0x0d, 0x48, 0x1c, 0x27, 0x65, 0x04, 0x90, 0x4a, 0xa9, 0x5f, 0x1c, 0x40, 0x85, 0xb2, + 0xee, 0xe9, 0x2b, 0xba, 0x4b, 0x84, 0xd3, 0xf4, 0x98, 0x0f, 0xf3, 0x3f, 0x47, 0x90, 0x83, 0xb1, + 0x92, 0xf0, 0x35, 0xd1, 0x02, 0xf8, 0x13, 0x21, 0xdf, 0x20, 0x5f, 0x93, 0x98, 0x00, 0x62, 0xa5, + 0xde, 0xe6, 0x60, 0x2d, 0x46, 0x88, 0xaf, 0xa2, 0x21, 0x1f, 0x46, 0x0f, 0x00, 0xb9, 0x50, 0x67, + 0x8c, 0x15, 0xba, 0xff, 0xd7, 0x44, 0x34, 0x7e, 0x11, 0x0d, 0xfb, 0x3f, 0x85, 0xad, 0x35, 0xcb, + 0x66, 0xb1, 0x12, 0x3b, 0x3d, 0x89, 0xa4, 0x62, 0x51, 0x98, 0xdf, 0x7a, 0xa5, 0xa2, 0x91, 0x84, + 0x11, 0x12, 0x29, 0xfe, 0x1c, 0x1a, 0xf5, 0x7f, 0xf3, 0x03, 0x03, 0xcb, 0xad, 0x71, 0x35, 0xc8, + 0x4b, 0x17, 0x11, 0xeb, 0xa4, 0x4c, 0xce, 0x8e, 0x0e, 0x8f, 0xfb, 0x39, 0x10, 0x8c, 0x95, 0xf8, + 0xc9, 0x21, 0x52, 0x01, 0xae, 0xa0, 0x71, 0x1f, 0x12, 0x6a, 0x68, 0x7f, 0x78, 0x62, 0x34, 0x56, + 0xea, 0x89, 0x4a, 0x1a, 0x2f, 0x85, 0x9b, 0xe8, 0xbc, 0x04, 0x34, 0xdc, 0x35, 0xf3, 0xbe, 0xc7, + 0x8f, 0x7b, 0x3c, 0x7e, 0x1b, 0x4f, 0x7a, 0x13, 0x70, 0x65, 0x34, 0x7e, 0xf6, 0x2a, 0x39, 0xb2, + 0x7e, 0x57, 0x6e, 0xb8, 0x86, 0x4e, 0xfb, 0xf8, 0x5b, 0xd3, 0xd5, 0xaa, 0x63, 0xbf, 0x43, 0x1a, + 0x5e, 0xa5, 0xcc, 0x8f, 0xcb, 0x10, 0xd7, 0xc3, 0x58, 0xa9, 0xaf, 0x36, 0xda, 0x54, 0x29, 0x28, + 0x4e, 0x66, 0x9e, 0x58, 0x18, 0xdf, 0x45, 0x67, 0x04, 0x78, 0xc5, 0x72, 0x3d, 0xdd, 0x6a, 0x90, + 0x4a, 0x99, 0x9f, 0xa1, 0xe1, 0x3c, 0xcf, 0xb9, 0x9a, 0x1c, 0x29, 0xb3, 0x4d, 0x2e, 0x8e, 0x5f, + 0x46, 0x23, 0x3e, 0x82, 0xbd, 0x7f, 0x0c, 0xc1, 0xfb, 0x07, 0x0c, 0x49, 0x63, 0xa5, 0x1e, 0x75, + 0x52, 0x91, 0x89, 0x45, 0x8d, 0x82, 0xb4, 0xa0, 0xc3, 0x92, 0x46, 0x79, 0x9b, 0xed, 0x44, 0x65, + 0x84, 0x54, 0xa1, 0xaf, 0x86, 0x1a, 0xb5, 0xe4, 0x98, 0xab, 0x26, 0x3b, 0x49, 0xfb, 0x7e, 0x29, + 0x2b, 0x75, 0x1b, 0x80, 0x49, 0xfa, 0xc1, 0xc8, 0x27, 0x4a, 0xe8, 0x54, 0x82, 0x8e, 0xed, 0xe9, + 0xc4, 0xf8, 0x85, 0x6c, 0xd8, 0x88, 0x63, 0x7e, 0x6c, 0x9c, 0x42, 0x03, 0xfe, 0x97, 0xf0, 0xcd, + 0x83, 0x92, 0x36, 0x34, 0xa3, 0x3c, 0x7c, 0xbc, 0x24, 0x8e, 0x63, 0x7e, 0x94, 0x3c, 0x0c, 0x71, + 0xbc, 0x97, 0x09, 0xc5, 0x71, 0xcc, 0x8f, 0x97, 0x7f, 0x3d, 0x1f, 0xce, 0x49, 0x27, 0x67, 0xcc, + 0xc3, 0xda, 0x26, 0x87, 0xe6, 0x45, 0x7d, 0x7b, 0xf0, 0x0f, 0x11, 0x55, 0xb3, 0x7f, 0x7f, 0xaa, + 0x89, 0x5f, 0x46, 0x43, 0x55, 0xdb, 0xf5, 0x56, 0x1d, 0xe2, 0x56, 0x83, 0xf8, 0xa3, 0xe0, 0x5b, + 0xd4, 0xe6, 0xe0, 0x7a, 0x5b, 0x9a, 0xfd, 0x45, 0x72, 0xf5, 0x0f, 0x72, 0x31, 0x6d, 0x60, 0x1b, + 0xd7, 0x63, 0xa9, 0x0d, 0x87, 0x30, 0xd4, 0xf1, 0xf5, 0x70, 0x15, 0x64, 0x3b, 0xfc, 0x5e, 0x21, + 0xb8, 0xca, 0x0a, 0xdf, 0xe0, 0xcb, 0x24, 0xf8, 0xd3, 0xe8, 0x9c, 0x04, 0xa8, 0xea, 0x8e, 0xde, + 0x22, 0x5e, 0x98, 0xeb, 0x05, 0xdc, 0xe5, 0xfd, 0xd2, 0xf5, 0x76, 0x80, 0x16, 0xf3, 0xc7, 0xa4, + 0x70, 0x10, 0x54, 0xab, 0x7f, 0x0f, 0x96, 0x6b, 0xff, 0x39, 0x8b, 0x46, 0x82, 0x8e, 0xd6, 0x1d, + 0x97, 0x3c, 0xba, 0x3d, 0xfa, 0x71, 0x34, 0x02, 0xde, 0x9b, 0x2d, 0x62, 0x79, 0x42, 0x52, 0x45, + 0x16, 0xf0, 0xd1, 0x47, 0xf0, 0xd8, 0xbe, 0x12, 0x21, 0x2e, 0xa2, 0x5e, 0xa6, 0x03, 0x82, 0x4f, + 0x2d, 0x53, 0x00, 0x06, 0x57, 0x7f, 0x22, 0x87, 0x86, 0x7d, 0x29, 0x4f, 0x99, 0xc7, 0xf5, 0xc6, + 0xe7, 0x68, 0x85, 0x7c, 0x0d, 0xa1, 0xaa, 0xed, 0x78, 0x7a, 0x53, 0x48, 0xcd, 0x0e, 0x47, 0xa5, + 0x36, 0x40, 0x59, 0x19, 0x81, 0x04, 0x4f, 0x22, 0x24, 0x0c, 0xb0, 0x7e, 0x18, 0x60, 0xa3, 0xdb, + 0x5b, 0x45, 0x14, 0x8e, 0x2b, 0x4d, 0xa0, 0x50, 0x7f, 0x3d, 0x8b, 0xc6, 0xfc, 0x4e, 0x9a, 0x79, + 0x48, 0x1a, 0x1d, 0xef, 0x11, 0x1e, 0x0c, 0xb2, 0xb4, 0x7b, 0x77, 0x94, 0xb6, 0xfa, 0x3f, 0x84, + 0x89, 0x64, 0xba, 0x69, 0x9f, 0x4c, 0x24, 0x7f, 0x11, 0x3a, 0xae, 0x7e, 0x6f, 0x0e, 0x9d, 0xf6, + 0xa5, 0x3e, 0xdb, 0xb1, 0x60, 0x93, 0x31, 0xad, 0x37, 0x9b, 0x8f, 0xf2, 0xba, 0x3c, 0xe4, 0x0b, + 0x62, 0x89, 0x87, 0x43, 0xe0, 0x71, 0xd6, 0xef, 0x73, 0x70, 0xdd, 0x36, 0x0d, 0x4d, 0x24, 0xc2, + 0xaf, 0xa2, 0x61, 0xff, 0x67, 0xc9, 0x59, 0xf5, 0x17, 0x63, 0xb8, 0x32, 0x08, 0x0a, 0xe9, 0x8e, + 0xe4, 0xf5, 0x21, 0x15, 0x50, 0xff, 0x4b, 0x1f, 0x9a, 0xb8, 0x67, 0x5a, 0x86, 0xbd, 0xe1, 0xfa, + 0x61, 0xfa, 0x8f, 0xfd, 0x96, 0xf9, 0xa8, 0xc3, 0xf3, 0xbf, 0x81, 0xce, 0x44, 0x45, 0xea, 0x04, + 0xc1, 0x93, 0x78, 0xef, 0x6c, 0x30, 0x82, 0xba, 0x1f, 0xb0, 0x9f, 0xdf, 0xbb, 0x69, 0xc9, 0x25, + 0xa3, 0x11, 0xff, 0xfb, 0x77, 0x13, 0xf1, 0xff, 0x19, 0xd4, 0x57, 0xb6, 0x5b, 0xba, 0xe9, 0xfb, + 0xff, 0xc1, 0x28, 0x0e, 0xea, 0x05, 0x8c, 0xc6, 0x29, 0x28, 0x7f, 0x5e, 0x31, 0x74, 0xd9, 0x60, + 0xc8, 0xdf, 0x2f, 0xd0, 0x71, 0x89, 0xa3, 0x89, 0x44, 0xd8, 0x46, 0x23, 0xbc, 0x3a, 0x7e, 0x4b, + 0x86, 0xe0, 0x96, 0x2c, 0xc8, 0xab, 0x98, 0xae, 0x56, 0x93, 0x52, 0x39, 0x76, 0x5d, 0xc6, 0x12, + 0x11, 0xf0, 0x8f, 0x61, 0xf7, 0x65, 0x9a, 0xcc, 0x5f, 0x10, 0x02, 0x4c, 0x32, 0x43, 0x71, 0x21, + 0xc0, 0x2c, 0x23, 0x12, 0xe1, 0x19, 0x34, 0x5e, 0x6a, 0x36, 0xed, 0x8d, 0x20, 0x4a, 0x11, 0x55, + 0x89, 0x61, 0x88, 0xd4, 0x0a, 0x97, 0x2f, 0x3a, 0x45, 0xc2, 0xc7, 0xd5, 0x1b, 0x1c, 0xad, 0xc5, + 0x4b, 0x4c, 0xbc, 0x86, 0x70, 0xbc, 0xcd, 0x7b, 0xba, 0x7e, 0xf9, 0x62, 0x16, 0xe1, 0xc8, 0x39, + 0x64, 0xe6, 0x11, 0xde, 0x4e, 0xa9, 0x3f, 0x9f, 0x41, 0xe3, 0xb1, 0xe8, 0x61, 0xf8, 0x06, 0x42, + 0x0c, 0x22, 0x44, 0xad, 0x00, 0x37, 0xb0, 0x30, 0xa2, 0x18, 0x5f, 0x4a, 0x42, 0x32, 0x7c, 0x0d, + 0x0d, 0xb0, 0x5f, 0x41, 0x9a, 0xd0, 0x68, 0x91, 0x4e, 0xc7, 0x34, 0xb4, 0x80, 0x28, 0xac, 0x05, + 0xee, 0xf1, 0x72, 0x89, 0x45, 0xbc, 0xcd, 0x76, 0x50, 0x0b, 0x25, 0xa3, 0x1d, 0x38, 0x1c, 0x34, + 0xb8, 0x64, 0x1c, 0x55, 0xd7, 0xf5, 0xf1, 0x40, 0x6c, 0xb9, 0x9d, 0x02, 0xb1, 0x45, 0xe6, 0x26, + 0x1e, 0x79, 0xed, 0xf0, 0x8c, 0x4b, 0xbf, 0x9c, 0x45, 0x63, 0x41, 0xad, 0x47, 0x78, 0x65, 0xf4, + 0x01, 0x12, 0xc9, 0x97, 0x32, 0x48, 0x99, 0x32, 0x9b, 0x4d, 0xd3, 0x5a, 0xad, 0x58, 0xf7, 0x6d, + 0xa7, 0x05, 0x93, 0xc7, 0xd1, 0xdd, 0x2e, 0xaa, 0x3f, 0x90, 0x41, 0xe3, 0xbc, 0x41, 0xd3, 0xba, + 0x63, 0x1c, 0xdd, 0xb5, 0x6f, 0xb4, 0x25, 0x47, 0xa7, 0x2f, 0xea, 0x57, 0xb3, 0x08, 0xcd, 0xdb, + 0x8d, 0xf5, 0x63, 0x6e, 0x41, 0xff, 0x89, 0x9d, 0xb3, 0xe3, 0x16, 0xe4, 0xec, 0xb8, 0x4a, 0xc6, + 0xcf, 0x8f, 0x4b, 0x2b, 0xa5, 0x74, 0x7c, 0x57, 0x13, 0x54, 0x2a, 0xa6, 0xdf, 0x65, 0x95, 0x6e, + 0x6f, 0x15, 0xf3, 0x4d, 0xbb, 0xb1, 0xae, 0x01, 0xbd, 0xfa, 0xe7, 0x19, 0x26, 0xbb, 0x63, 0x6e, + 0x61, 0xef, 0x7f, 0x7e, 0x7e, 0x8f, 0x9f, 0xff, 0xd7, 0x32, 0xe8, 0xb4, 0x46, 0x1a, 0xf6, 0x03, + 0xe2, 0x6c, 0x4e, 0xdb, 0x06, 0xb9, 0x45, 0x2c, 0xe2, 0x1c, 0xd5, 0x88, 0xfa, 0xc7, 0x10, 0x6a, + 0x32, 0x6c, 0xcc, 0x1d, 0x97, 0x18, 0xc7, 0x27, 0xe0, 0xa9, 0xfa, 0x8f, 0xfa, 0x91, 0x92, 0xb8, + 0x43, 0x3c, 0xb6, 0xbb, 0xa2, 0xd4, 0x6d, 0x7f, 0xfe, 0xb0, 0xb6, 0xfd, 0xbd, 0x7b, 0xdb, 0xf6, + 0xf7, 0xed, 0x75, 0xdb, 0xdf, 0xbf, 0x9b, 0x6d, 0x7f, 0x2b, 0xba, 0xed, 0x1f, 0x80, 0x6d, 0xff, + 0x8d, 0xae, 0xdb, 0xfe, 0x19, 0xcb, 0xd8, 0xe7, 0xa6, 0xff, 0xd8, 0xa6, 0xf9, 0xd8, 0xcf, 0x69, + 0xe5, 0x32, 0x9d, 0x14, 0x1b, 0xb6, 0x63, 0x10, 0x83, 0x1f, 0x52, 0xe0, 0x56, 0xde, 0xe1, 0x30, + 0x2d, 0xc0, 0xc6, 0x72, 0xa6, 0x8c, 0xec, 0x26, 0x67, 0xca, 0x21, 0x1c, 0x63, 0xbe, 0x90, 0x45, + 0xe3, 0xd3, 0xc4, 0xf1, 0x58, 0x3c, 0x91, 0xc3, 0x78, 0x48, 0x2e, 0xa1, 0x31, 0x81, 0x21, 0xec, + 0xc8, 0x85, 0x5c, 0xff, 0x0d, 0xe2, 0x78, 0xd1, 0xb7, 0xf5, 0x28, 0x3d, 0xad, 0xde, 0x8f, 0x5b, + 0xcc, 0xc7, 0x6e, 0x50, 0xbd, 0x0f, 0x67, 0x82, 0x34, 0xf9, 0x2f, 0x2d, 0xa0, 0x17, 0x42, 0x11, + 0xe7, 0xf7, 0x1e, 0x8a, 0x58, 0xfd, 0xd9, 0x0c, 0xba, 0xa4, 0x11, 0x8b, 0x6c, 0xe8, 0x2b, 0x4d, + 0x22, 0x34, 0x8b, 0xaf, 0x0c, 0x74, 0xd6, 0x30, 0xdd, 0x96, 0xee, 0x35, 0xd6, 0x0e, 0x24, 0xa3, + 0x59, 0x34, 0x2c, 0xce, 0x5f, 0x7b, 0x98, 0xdb, 0xa4, 0x72, 0xea, 0xaf, 0xe5, 0x50, 0xff, 0x94, + 0xed, 0x1d, 0x38, 0x63, 0x78, 0x38, 0xe5, 0x67, 0xf7, 0x70, 0x2f, 0xf2, 0x51, 0xa8, 0x5c, 0x88, + 0x24, 0x08, 0x86, 0x17, 0x2b, 0x76, 0x2c, 0xe2, 0xa2, 0x4f, 0xb6, 0xc7, 0xa8, 0xd8, 0xcf, 0xa3, + 0x41, 0xf0, 0xf6, 0x14, 0x6e, 0x2e, 0xc1, 0xac, 0xc9, 0xa3, 0xc0, 0x68, 0x1d, 0x21, 0x29, 0xfe, + 0xb4, 0x14, 0x00, 0xa5, 0xef, 0xe0, 0x51, 0xb4, 0xc5, 0x58, 0x28, 0x87, 0x16, 0xac, 0x5a, 0xfd, + 0x56, 0x1e, 0x0d, 0xfb, 0xc6, 0x2c, 0x47, 0xd4, 0x83, 0xcf, 0xa2, 0xbe, 0x39, 0x5b, 0x88, 0x8a, + 0x08, 0xc6, 0x2f, 0x6b, 0xb6, 0x1b, 0xb1, 0xea, 0xe1, 0x44, 0xf8, 0x06, 0x1a, 0x58, 0xb4, 0x0d, + 0xd1, 0x74, 0x0b, 0xc6, 0xb4, 0x65, 0x1b, 0x31, 0xd7, 0x97, 0x80, 0x10, 0x5f, 0x42, 0x79, 0xb0, + 0x7a, 0x13, 0xae, 0x9e, 0x23, 0x96, 0x6e, 0x80, 0x17, 0x74, 0xa3, 0x6f, 0xaf, 0xba, 0xd1, 0xbf, + 0x5f, 0xdd, 0x18, 0x38, 0x5c, 0xdd, 0x78, 0x0b, 0x0d, 0x43, 0x4d, 0x7e, 0xd4, 0xef, 0x9d, 0x97, + 0xb7, 0xc7, 0xf8, 0x0a, 0x34, 0xc2, 0xda, 0xcd, 0x63, 0x7f, 0xc3, 0xc2, 0x23, 0xb1, 0x8a, 0xa8, + 0x1d, 0x3a, 0x80, 0xda, 0xfd, 0x41, 0x06, 0xf5, 0xdf, 0xb1, 0xd6, 0x2d, 0x7b, 0xe3, 0x60, 0x1a, + 0x77, 0x03, 0x0d, 0x71, 0x36, 0xc2, 0x1c, 0x0f, 0xde, 0x4c, 0x1d, 0x06, 0xae, 0x03, 0x27, 0x4d, + 0xa4, 0xc2, 0x2f, 0x07, 0x85, 0xc0, 0xb0, 0x35, 0x17, 0xc6, 0x15, 0xf5, 0x0b, 0x35, 0xe4, 0x50, + 0x88, 0x22, 0x39, 0x3e, 0xcf, 0x73, 0xe0, 0x0b, 0x81, 0x75, 0x68, 0x53, 0x58, 0x0a, 0x7c, 0xf5, + 0x5f, 0x66, 0xd1, 0x68, 0xe4, 0xfa, 0xe9, 0x19, 0x34, 0xc8, 0xaf, 0x7f, 0x4c, 0x3f, 0x36, 0x23, + 0x18, 0xbe, 0x06, 0x40, 0x6d, 0x80, 0xfd, 0x59, 0x31, 0xf0, 0x27, 0x51, 0xbf, 0xed, 0xc2, 0xd2, + 0x04, 0xdf, 0x32, 0x1a, 0x0e, 0xa1, 0xa5, 0x1a, 0x6d, 0x3b, 0x1b, 0x1c, 0x9c, 0x44, 0xd4, 0x48, + 0xdb, 0x85, 0x4f, 0xbb, 0x89, 0x06, 0x75, 0xd7, 0x25, 0x5e, 0xdd, 0xd3, 0x57, 0xc5, 0x70, 0x8d, + 0x01, 0x50, 0x1c, 0x1d, 0x00, 0x5c, 0xd6, 0x57, 0xf1, 0x6b, 0x68, 0xa4, 0xe1, 0x10, 0x58, 0xbc, + 0xf4, 0x26, 0x6d, 0xa5, 0xb0, 0xb9, 0x94, 0x10, 0xe2, 0x8d, 0x7f, 0x88, 0xa8, 0x18, 0xf8, 0x2e, + 0x1a, 0xe1, 0x9f, 0xc3, 0xac, 0xce, 0x60, 0xa0, 0x8d, 0x86, 0x8b, 0x09, 0x13, 0x09, 0xb3, 0x3b, + 0xe3, 0xc6, 0x87, 0x22, 0xb9, 0xc8, 0xd7, 0x10, 0x48, 0xd5, 0xaf, 0x67, 0xe8, 0x86, 0x87, 0x02, + 0x82, 0x74, 0x9a, 0xad, 0x3d, 0xea, 0x4a, 0x2b, 0x8c, 0x98, 0xdf, 0xe7, 0x76, 0x99, 0x9d, 0x34, + 0x8e, 0xc5, 0x93, 0xa8, 0xcf, 0x10, 0xef, 0x7e, 0xce, 0xca, 0x1f, 0xe1, 0xd7, 0xa3, 0x71, 0x2a, + 0x7c, 0x19, 0xe5, 0xe9, 0x86, 0x36, 0x7a, 0xf0, 0x13, 0xd7, 0x48, 0x0d, 0x28, 0xd4, 0xef, 0xce, + 0xa2, 0x61, 0xe1, 0x6b, 0xae, 0x1f, 0xe8, 0x73, 0x5e, 0xda, 0x5d, 0x33, 0xb9, 0x1d, 0x2c, 0xc0, + 0x82, 0x26, 0xdf, 0x0c, 0x44, 0xb1, 0xab, 0x27, 0x08, 0x2e, 0x98, 0xe7, 0xf9, 0x87, 0xf6, 0xed, + 0xfe, 0x10, 0x44, 0xe9, 0x5f, 0xcf, 0x0f, 0x64, 0x0b, 0xb9, 0xd7, 0xf3, 0x03, 0xf9, 0x42, 0x2f, + 0x78, 0xd6, 0x43, 0x34, 0x29, 0x76, 0xc2, 0xb4, 0xee, 0x9b, 0xab, 0xc7, 0xdc, 0x6e, 0xf0, 0x70, + 0xa3, 0x0e, 0x44, 0x64, 0x73, 0xcc, 0x8d, 0x08, 0xdf, 0x57, 0xd9, 0x9c, 0x24, 0x30, 0xe0, 0xb2, + 0xf9, 0x77, 0x19, 0xa4, 0x24, 0xca, 0xa6, 0x74, 0x44, 0x2f, 0xdf, 0x87, 0x97, 0xc6, 0xe0, 0x9b, + 0x59, 0x34, 0x5e, 0xb1, 0x3c, 0xb2, 0xca, 0xce, 0x3d, 0xc7, 0x7c, 0xaa, 0xb8, 0xcd, 0xd2, 0x98, + 0xf2, 0x8f, 0xe1, 0x7d, 0xfe, 0x78, 0x70, 0xaa, 0x0c, 0x51, 0x29, 0x9c, 0xc4, 0xd2, 0x87, 0x98, + 0xde, 0x28, 0x22, 0xe4, 0x63, 0x3e, 0xe7, 0x1c, 0x0f, 0x21, 0x1f, 0xf3, 0xc9, 0xeb, 0x03, 0x2a, + 0xe4, 0xff, 0x9e, 0x41, 0xa7, 0x12, 0x2a, 0x87, 0xe4, 0x80, 0x9d, 0x15, 0x08, 0xb9, 0x90, 0x11, + 0x92, 0x03, 0x76, 0x56, 0x20, 0xda, 0x82, 0xe6, 0x23, 0xf1, 0x32, 0x38, 0x56, 0x2d, 0x55, 0xca, + 0xd3, 0x5c, 0xaa, 0xaa, 0xe0, 0x22, 0x46, 0xc1, 0x49, 0x5f, 0x16, 0x38, 0x5f, 0xd9, 0xa6, 0xd1, + 0x88, 0x38, 0x5f, 0xd1, 0x32, 0xf8, 0x33, 0x68, 0xb0, 0xf4, 0x6e, 0xc7, 0x21, 0xc0, 0x97, 0x49, + 0xfc, 0x43, 0x01, 0x5f, 0x1f, 0x91, 0xc4, 0x99, 0xf9, 0x91, 0x51, 0x8a, 0x28, 0xef, 0x90, 0xa1, + 0xfa, 0xc5, 0x0c, 0x9a, 0x48, 0x6f, 0x1d, 0xfe, 0x28, 0xea, 0xa7, 0x27, 0xdb, 0x92, 0xb6, 0xc8, + 0x3f, 0x9d, 0xa5, 0xfc, 0xb0, 0x9b, 0xa4, 0xae, 0x3b, 0xe2, 0xc6, 0xdb, 0x27, 0xc3, 0xaf, 0xa0, + 0xa1, 0x8a, 0xeb, 0x76, 0x88, 0x53, 0xbb, 0x71, 0x47, 0xab, 0xf0, 0x33, 0x15, 0xec, 0xd9, 0x4d, + 0x00, 0xd7, 0xdd, 0x1b, 0x91, 0xa0, 0x0a, 0x22, 0xbd, 0xfa, 0x83, 0x19, 0x74, 0xbe, 0xdb, 0x57, + 0xd1, 0x03, 0xfc, 0x32, 0xb1, 0x74, 0xcb, 0xe3, 0xa9, 0x75, 0xf9, 0x11, 0xc5, 0x03, 0x98, 0x7c, + 0xc8, 0x08, 0x08, 0x69, 0x21, 0x76, 0x3b, 0x16, 0x3c, 0xc7, 0xb3, 0x9b, 0x3c, 0x80, 0x45, 0x0a, + 0xf9, 0x84, 0xea, 0xcf, 0xbc, 0x89, 0x7a, 0x97, 0x2c, 0xb2, 0x74, 0x1f, 0x3f, 0x27, 0x24, 0x70, + 0xe3, 0x03, 0x6d, 0x5c, 0x1c, 0x30, 0x80, 0x98, 0xeb, 0xd1, 0x84, 0x34, 0x6f, 0x37, 0xc5, 0x2c, + 0x54, 0x5c, 0x1d, 0xb0, 0x58, 0x86, 0x61, 0xe6, 0x7a, 0x34, 0x31, 0x5b, 0xd5, 0x4d, 0x31, 0xb9, + 0x12, 0xef, 0x6c, 0xa9, 0x14, 0xc3, 0xf8, 0xa5, 0xf8, 0x34, 0x30, 0x9f, 0x94, 0x81, 0x28, 0xba, + 0x27, 0x88, 0x53, 0xcc, 0xf5, 0x68, 0xc9, 0x99, 0x8b, 0x86, 0x45, 0xc3, 0x98, 0xe8, 0x83, 0x9c, + 0x88, 0x9b, 0xeb, 0xd1, 0x24, 0x5a, 0xfc, 0x42, 0x90, 0xe6, 0xf1, 0x75, 0xdb, 0xb4, 0xa2, 0xde, + 0x95, 0x02, 0x6a, 0xae, 0x47, 0x13, 0x29, 0x85, 0x4a, 0xab, 0x8e, 0x19, 0xe4, 0x60, 0x8b, 0x56, + 0x0a, 0x38, 0xa1, 0x52, 0xf8, 0x8d, 0x5f, 0x41, 0x23, 0x81, 0xdb, 0xea, 0x3b, 0xa4, 0xe1, 0xf1, + 0x2b, 0x91, 0x33, 0x91, 0xc2, 0x0c, 0x39, 0xd7, 0xa3, 0xc9, 0xd4, 0xf8, 0xb2, 0x9f, 0xe0, 0x9f, + 0xdf, 0x75, 0x8c, 0x0a, 0xd3, 0x99, 0xf9, 0x2e, 0x95, 0x12, 0xc7, 0xd3, 0xde, 0x09, 0xdf, 0x0e, + 0xf8, 0x05, 0x06, 0x8e, 0xd4, 0x32, 0x63, 0x19, 0xb4, 0x77, 0x84, 0x87, 0xa3, 0xd7, 0xa2, 0x29, + 0x90, 0x79, 0x62, 0xed, 0xb3, 0x91, 0x92, 0x1c, 0x3b, 0xd7, 0xa3, 0x45, 0x53, 0x26, 0xbf, 0x20, + 0xa5, 0xdf, 0xe5, 0xf1, 0x53, 0xa2, 0x52, 0xa5, 0x28, 0x41, 0xaa, 0x90, 0xa8, 0xf7, 0xb5, 0x68, + 0x3e, 0x58, 0x1e, 0x2d, 0xe5, 0x6c, 0x72, 0xd6, 0x50, 0xa1, 0x6a, 0x3f, 0x7f, 0xec, 0x0b, 0x52, + 0xde, 0x4e, 0x48, 0x8d, 0x9d, 0x50, 0xb5, 0xee, 0xe9, 0x62, 0xd5, 0xec, 0x7c, 0x29, 0x65, 0x90, + 0x84, 0x04, 0x37, 0xf1, 0x0e, 0x05, 0x9c, 0xd0, 0xa1, 0x2c, 0xdb, 0xe4, 0x0b, 0x52, 0x12, 0x13, + 0x9e, 0xc1, 0x26, 0xa8, 0x54, 0x40, 0xd1, 0x4a, 0xc5, 0x74, 0x27, 0x37, 0xc5, 0xdc, 0x1e, 0xca, + 0xb8, 0xdc, 0x41, 0x21, 0x86, 0x76, 0x90, 0x90, 0x03, 0xa4, 0x08, 0x79, 0x03, 0x14, 0x0c, 0xe4, + 0x43, 0x41, 0x0b, 0xa7, 0xab, 0x73, 0x3d, 0x1a, 0x64, 0x14, 0x50, 0x59, 0x46, 0x0a, 0xe5, 0x14, + 0x50, 0x0c, 0x07, 0xf9, 0x51, 0x1f, 0x92, 0xc6, 0x5c, 0x8f, 0xc6, 0xb2, 0x55, 0x3c, 0x27, 0xc4, + 0x7e, 0x56, 0x4e, 0xcb, 0x53, 0x44, 0x80, 0xa0, 0x53, 0x44, 0x18, 0x21, 0x7a, 0x36, 0x1e, 0x1f, + 0x59, 0x39, 0x23, 0xaf, 0xa8, 0x51, 0xfc, 0x5c, 0x8f, 0x16, 0x8f, 0xa9, 0xfc, 0x82, 0x14, 0x32, + 0x58, 0x39, 0x1b, 0x71, 0x69, 0x0e, 0x51, 0x54, 0x5c, 0x62, 0x70, 0xe1, 0xa5, 0xc4, 0x24, 0x5f, + 0xca, 0x39, 0x79, 0x39, 0x4e, 0x20, 0x99, 0xeb, 0xd1, 0x12, 0xd3, 0x83, 0x4d, 0xc7, 0x02, 0xf7, + 0x2a, 0x8a, 0xfc, 0x6e, 0x19, 0x41, 0xcf, 0xf5, 0x68, 0xb1, 0x50, 0xbf, 0x37, 0xc5, 0x88, 0xb9, + 0xca, 0x63, 0x72, 0x27, 0x86, 0x18, 0xda, 0x89, 0x42, 0x64, 0xdd, 0x9b, 0x62, 0x58, 0x59, 0x65, + 0x22, 0x5e, 0x2a, 0x9c, 0x39, 0x85, 0xf0, 0xb3, 0x5a, 0x72, 0xec, 0x55, 0xe5, 0x71, 0x1e, 0x9a, + 0x9f, 0x97, 0x4f, 0xa2, 0x99, 0xeb, 0xd1, 0x92, 0xe3, 0xb6, 0x6a, 0xc9, 0x41, 0x4b, 0x95, 0xf3, + 0xdd, 0x78, 0x06, 0xad, 0x4b, 0x0e, 0x78, 0xaa, 0x77, 0x09, 0x21, 0xa9, 0x5c, 0x90, 0xe3, 0x29, + 0xa5, 0x12, 0xce, 0xf5, 0x68, 0x5d, 0x02, 0x51, 0xde, 0x49, 0x89, 0xe7, 0xa8, 0x5c, 0x94, 0x33, + 0x73, 0x24, 0x12, 0xcd, 0xf5, 0x68, 0x29, 0xd1, 0x20, 0xef, 0xa4, 0x84, 0x42, 0x54, 0x8a, 0x5d, + 0xd9, 0x06, 0xf2, 0x48, 0x09, 0xa4, 0xb8, 0x94, 0x18, 0x45, 0x50, 0x79, 0x42, 0x56, 0xdd, 0x04, + 0x12, 0xaa, 0xba, 0x49, 0xf1, 0x07, 0x97, 0x12, 0xc3, 0xf8, 0x29, 0x4f, 0x76, 0x61, 0x18, 0xb4, + 0x31, 0x31, 0x00, 0xe0, 0x52, 0x62, 0x1c, 0x3d, 0x45, 0x95, 0x19, 0x26, 0x90, 0x50, 0x86, 0x49, + 0x11, 0xf8, 0x96, 0x12, 0x03, 0xd9, 0x29, 0x4f, 0x75, 0x61, 0x18, 0xb6, 0x30, 0x29, 0x04, 0xde, + 0x0b, 0x52, 0x24, 0x39, 0xe5, 0x43, 0xf2, 0xbc, 0x21, 0xa0, 0xe8, 0xbc, 0x21, 0xc6, 0x9c, 0x9b, + 0x8e, 0xc5, 0xca, 0x51, 0x3e, 0x2c, 0x0f, 0xf3, 0x08, 0x9a, 0x0e, 0xf3, 0x68, 0x74, 0x9d, 0xe9, + 0x58, 0xcc, 0x10, 0xe5, 0x52, 0x1a, 0x13, 0x40, 0xcb, 0x4c, 0x58, 0x94, 0x91, 0x4a, 0x42, 0xd0, + 0x0a, 0xe5, 0x69, 0xd9, 0xe6, 0x2e, 0x46, 0x30, 0xd7, 0xa3, 0x25, 0x84, 0xba, 0xd0, 0x92, 0x3d, + 0x34, 0x95, 0xcb, 0xf2, 0xb0, 0x4d, 0xa2, 0xa1, 0xc3, 0x36, 0xd1, 0xbb, 0x73, 0x3e, 0xc9, 0xbe, + 0x56, 0xb9, 0x22, 0x6f, 0xcc, 0xe2, 0x14, 0x74, 0x63, 0x96, 0x60, 0x97, 0xab, 0x25, 0x7b, 0x0d, + 0x2a, 0xcf, 0x74, 0x6d, 0x21, 0xd0, 0x24, 0xb4, 0x90, 0x39, 0xd1, 0x85, 0x7b, 0xa7, 0x3b, 0xed, + 0xa6, 0xad, 0x1b, 0xca, 0x47, 0x12, 0xf7, 0x4e, 0x0c, 0x29, 0xec, 0x9d, 0x18, 0x80, 0xae, 0xf2, + 0xa2, 0xfd, 0xa9, 0x72, 0x55, 0x5e, 0xe5, 0x45, 0x1c, 0x5d, 0xe5, 0x25, 0x5b, 0xd5, 0xe9, 0x98, + 0xad, 0xa6, 0xf2, 0xac, 0xac, 0x00, 0x11, 0x34, 0x55, 0x80, 0xa8, 0x75, 0xe7, 0xdb, 0xe9, 0xd6, + 0x8d, 0xca, 0x24, 0x70, 0x7b, 0x22, 0xc8, 0x00, 0x9f, 0x42, 0x37, 0xd7, 0xa3, 0xa5, 0x5b, 0x48, + 0x56, 0x12, 0x8c, 0x15, 0x95, 0x6b, 0xb2, 0x82, 0xc5, 0x08, 0xa8, 0x82, 0xc5, 0x4d, 0x1c, 0x2b, + 0x09, 0xd6, 0x86, 0xca, 0x47, 0x53, 0x59, 0x05, 0xdf, 0x9c, 0x60, 0xa3, 0x78, 0x53, 0x34, 0x17, + 0x54, 0x9e, 0x93, 0x17, 0xbb, 0x10, 0x43, 0x17, 0x3b, 0xc1, 0xac, 0xf0, 0xa6, 0x68, 0x28, 0xa7, + 0x5c, 0x8f, 0x97, 0x0a, 0x97, 0x48, 0xc1, 0xa0, 0x4e, 0x4b, 0xb6, 0x2f, 0x53, 0x6e, 0xc8, 0x5a, + 0x97, 0x44, 0x43, 0xb5, 0x2e, 0xd1, 0x36, 0x6d, 0x36, 0x6e, 0x26, 0xa6, 0xdc, 0x8c, 0xde, 0x25, + 0xc8, 0x78, 0xba, 0xf3, 0x89, 0x99, 0x96, 0xbd, 0x16, 0x0d, 0x1f, 0xa0, 0x7c, 0x2c, 0xf2, 0x98, + 0x21, 0x61, 0xe9, 0xfe, 0x36, 0x12, 0x6e, 0xe0, 0xb5, 0xa8, 0xc7, 0xbd, 0xf2, 0x7c, 0x32, 0x87, + 0x40, 0x57, 0xa2, 0x1e, 0xfa, 0xaf, 0x45, 0x9d, 0xd4, 0x95, 0x17, 0x92, 0x39, 0x04, 0xd2, 0x8d, + 0x3a, 0xb5, 0x3f, 0x27, 0x84, 0xcd, 0x53, 0x3e, 0x2e, 0x6f, 0x1d, 0x03, 0x04, 0xdd, 0x3a, 0x86, + 0xc1, 0xf5, 0x9e, 0x13, 0xc2, 0xcd, 0x29, 0x2f, 0xc6, 0x8a, 0x04, 0x8d, 0x15, 0x82, 0xd2, 0x3d, + 0x27, 0x84, 0x69, 0x53, 0x5e, 0x8a, 0x15, 0x09, 0x5a, 0x27, 0x04, 0x73, 0x33, 0xba, 0xf9, 0xe1, + 0x28, 0x9f, 0x90, 0xaf, 0x38, 0xd2, 0x29, 0xe7, 0x7a, 0xb4, 0x6e, 0xfe, 0x3c, 0x6f, 0xa7, 0x1b, + 0xdd, 0x29, 0x2f, 0xcb, 0x43, 0x38, 0x8d, 0x8e, 0x0e, 0xe1, 0x54, 0xc3, 0xbd, 0x57, 0x22, 0x3e, + 0xb9, 0xca, 0x2b, 0xf2, 0x14, 0x27, 0x21, 0xe9, 0x14, 0x17, 0xf5, 0xe0, 0x95, 0x9c, 0x4d, 0x95, + 0x4f, 0xca, 0x53, 0x9c, 0x88, 0xa3, 0x53, 0x9c, 0xe4, 0x98, 0x3a, 0x1d, 0xf3, 0x81, 0x54, 0x5e, + 0x95, 0xa7, 0xb8, 0x08, 0x9a, 0x4e, 0x71, 0x51, 0xaf, 0xc9, 0x57, 0x22, 0xae, 0x80, 0xca, 0x6b, + 0xc9, 0xed, 0x07, 0xa4, 0xd8, 0x7e, 0xe6, 0x38, 0xa8, 0x25, 0xfb, 0xb4, 0x29, 0x25, 0x79, 0xfc, + 0x26, 0xd1, 0xd0, 0xf1, 0x9b, 0xe8, 0x0f, 0xb7, 0x94, 0x98, 0x17, 0x53, 0x99, 0xea, 0x72, 0x70, + 0x08, 0xb7, 0x22, 0x49, 0x19, 0x35, 0xc5, 0x33, 0x32, 0x3b, 0x08, 0x4d, 0xa7, 0x9c, 0x91, 0xfd, + 0x63, 0x50, 0x84, 0x9e, 0xce, 0xae, 0x31, 0x1b, 0x30, 0xa5, 0x2c, 0xcf, 0xae, 0x31, 0x02, 0x3a, + 0xbb, 0xc6, 0x2d, 0xc7, 0x66, 0x51, 0x81, 0x6b, 0x11, 0x33, 0x6d, 0x33, 0xad, 0x55, 0x65, 0x26, + 0xe2, 0x52, 0x12, 0xc1, 0xd3, 0xd9, 0x29, 0x0a, 0x83, 0xf5, 0x9a, 0xc1, 0xa6, 0x9b, 0x66, 0x7b, + 0xc5, 0xd6, 0x1d, 0xa3, 0x46, 0x2c, 0x43, 0x99, 0x8d, 0xac, 0xd7, 0x09, 0x34, 0xb0, 0x5e, 0x27, + 0xc0, 0xc1, 0xe9, 0x3d, 0x02, 0xd7, 0x48, 0x83, 0x98, 0x0f, 0x88, 0x72, 0x0b, 0xd8, 0x16, 0xd3, + 0xd8, 0x72, 0xb2, 0xb9, 0x1e, 0x2d, 0x8d, 0x03, 0xdd, 0xab, 0x2f, 0x6c, 0xd6, 0xde, 0x98, 0x0f, + 0xdc, 0x28, 0xab, 0x0e, 0x69, 0xeb, 0x0e, 0x51, 0xe6, 0xe4, 0xbd, 0x7a, 0x22, 0x11, 0xdd, 0xab, + 0x27, 0x22, 0xe2, 0x6c, 0xfd, 0xb1, 0x50, 0xe9, 0xc6, 0x36, 0x1c, 0x11, 0xc9, 0xa5, 0xe9, 0xec, + 0x24, 0x23, 0xa8, 0x80, 0xe6, 0x6d, 0x6b, 0x15, 0x6e, 0x2a, 0x5e, 0x97, 0x67, 0xa7, 0x74, 0x4a, + 0x3a, 0x3b, 0xa5, 0x63, 0xa9, 0xaa, 0xcb, 0x58, 0x36, 0x06, 0x6f, 0xcb, 0xaa, 0x9e, 0x40, 0x42, + 0x55, 0x3d, 0x01, 0x1c, 0x67, 0xa8, 0x11, 0x97, 0x78, 0xca, 0x7c, 0x37, 0x86, 0x40, 0x12, 0x67, + 0x08, 0xe0, 0x38, 0xc3, 0x59, 0xe2, 0x35, 0xd6, 0x94, 0x85, 0x6e, 0x0c, 0x81, 0x24, 0xce, 0x10, + 0xc0, 0xf4, 0xb0, 0x29, 0x83, 0xa7, 0x3a, 0xcd, 0x75, 0xbf, 0xcf, 0x16, 0xe5, 0xc3, 0x66, 0x2a, + 0x21, 0x3d, 0x6c, 0xa6, 0x22, 0xf1, 0x0f, 0xee, 0xda, 0x46, 0x51, 0x59, 0x82, 0x0a, 0x27, 0xc3, + 0x7d, 0xc1, 0x6e, 0x4a, 0xcd, 0xf5, 0x68, 0xbb, 0xb5, 0x81, 0xfc, 0x48, 0x60, 0x4a, 0xa4, 0x54, + 0xa1, 0xaa, 0xb1, 0xe0, 0xae, 0x82, 0x81, 0xe7, 0x7a, 0xb4, 0xc0, 0xd8, 0xe8, 0x05, 0x34, 0x04, + 0x1f, 0x55, 0xb1, 0x4c, 0xaf, 0x3c, 0xa5, 0xbc, 0x21, 0x1f, 0x99, 0x04, 0x14, 0x3d, 0x32, 0x09, + 0x3f, 0xe9, 0x24, 0x0e, 0x3f, 0xd9, 0x14, 0x53, 0x9e, 0x52, 0x34, 0x79, 0x12, 0x97, 0x90, 0x74, + 0x12, 0x97, 0x00, 0x41, 0xbd, 0x65, 0xc7, 0x6e, 0x97, 0xa7, 0x94, 0x5a, 0x42, 0xbd, 0x0c, 0x15, + 0xd4, 0xcb, 0x7e, 0x06, 0xf5, 0xd6, 0xd6, 0x3a, 0x5e, 0x99, 0x7e, 0xe3, 0x72, 0x42, 0xbd, 0x3e, + 0x32, 0xa8, 0xd7, 0x07, 0xd0, 0xa9, 0x10, 0x00, 0x55, 0xc7, 0xa6, 0x93, 0xf6, 0x6d, 0xb3, 0xd9, + 0x54, 0xee, 0xc8, 0x53, 0x61, 0x14, 0x4f, 0xa7, 0xc2, 0x28, 0x8c, 0x6e, 0x3d, 0x59, 0xab, 0xc8, + 0x4a, 0x67, 0x55, 0xb9, 0x2b, 0x6f, 0x3d, 0x43, 0x0c, 0xdd, 0x7a, 0x86, 0xbf, 0xe0, 0x74, 0x41, + 0x7f, 0x69, 0xe4, 0xbe, 0x43, 0xdc, 0x35, 0xe5, 0x5e, 0xe4, 0x74, 0x21, 0xe0, 0xe0, 0x74, 0x21, + 0xfc, 0xc6, 0xab, 0xe8, 0x71, 0x69, 0xa1, 0xf1, 0xdf, 0x9e, 0x6a, 0x44, 0x77, 0x1a, 0x6b, 0xca, + 0x9b, 0xc0, 0xea, 0xa9, 0xc4, 0xa5, 0x4a, 0x26, 0x9d, 0xeb, 0xd1, 0xba, 0x71, 0x82, 0x63, 0xf9, + 0x1b, 0xf3, 0x2c, 0xb6, 0x8d, 0x56, 0x9d, 0xf6, 0x0f, 0xa1, 0x6f, 0x45, 0x8e, 0xe5, 0x71, 0x12, + 0x38, 0x96, 0xc7, 0xc1, 0xb8, 0x8d, 0x2e, 0x46, 0x8e, 0x6a, 0x0b, 0x7a, 0x93, 0x9e, 0x4b, 0x88, + 0x51, 0xd5, 0x1b, 0xeb, 0xc4, 0x53, 0x3e, 0x05, 0xbc, 0x2f, 0xa5, 0x1c, 0xf8, 0x22, 0xd4, 0x73, + 0x3d, 0xda, 0x0e, 0xfc, 0xb0, 0xca, 0x32, 0x2f, 0x2a, 0x9f, 0x96, 0xef, 0x37, 0x29, 0x6c, 0xae, + 0x47, 0x63, 0x59, 0x19, 0xdf, 0x46, 0xca, 0x9d, 0xf6, 0xaa, 0xa3, 0x1b, 0x84, 0x6d, 0xb4, 0x60, + 0xef, 0xc6, 0x37, 0xa0, 0x9f, 0x91, 0x77, 0x69, 0x69, 0x74, 0x74, 0x97, 0x96, 0x86, 0xa3, 0x8a, + 0x2a, 0x85, 0x71, 0x55, 0x3e, 0x2b, 0x2b, 0xaa, 0x84, 0xa4, 0x8a, 0x2a, 0x07, 0x7d, 0x7d, 0x13, + 0x9d, 0x0d, 0xce, 0xf3, 0x7c, 0xfd, 0x65, 0x9d, 0xa6, 0xbc, 0x0d, 0x7c, 0x2e, 0xc6, 0x1e, 0x03, + 0x24, 0xaa, 0xb9, 0x1e, 0x2d, 0xa5, 0x3c, 0x5d, 0x71, 0x63, 0x11, 0xca, 0xf9, 0xf6, 0xe2, 0xbb, + 0xe4, 0x15, 0x37, 0x85, 0x8c, 0xae, 0xb8, 0x29, 0xa8, 0x44, 0xe6, 0x5c, 0xa8, 0xfa, 0x0e, 0xcc, + 0x03, 0x99, 0xa6, 0x71, 0x48, 0x64, 0xce, 0x77, 0x6a, 0x2b, 0x3b, 0x30, 0x0f, 0x76, 0x6b, 0x69, + 0x1c, 0xf0, 0x65, 0xd4, 0x57, 0xab, 0x2d, 0x68, 0x1d, 0x4b, 0x69, 0x44, 0x6c, 0xc0, 0x00, 0x3a, + 0xd7, 0xa3, 0x71, 0x3c, 0xdd, 0x06, 0xcd, 0x34, 0x75, 0xd7, 0x33, 0x1b, 0x2e, 0x8c, 0x18, 0x7f, + 0x84, 0x18, 0xf2, 0x36, 0x28, 0x89, 0x86, 0x6e, 0x83, 0x92, 0xe0, 0x74, 0xbf, 0x38, 0xad, 0xbb, + 0xae, 0x6e, 0x19, 0x8e, 0x3e, 0x05, 0xcb, 0x04, 0x89, 0x58, 0xca, 0x4b, 0x58, 0xba, 0x5f, 0x94, + 0x21, 0x70, 0xf9, 0xee, 0x43, 0xfc, 0x6d, 0xce, 0xfd, 0xc8, 0xe5, 0x7b, 0x04, 0x0f, 0x97, 0xef, + 0x11, 0x18, 0xec, 0x3b, 0x7d, 0x98, 0x46, 0x56, 0x4d, 0xc8, 0x93, 0xbc, 0x1a, 0xd9, 0x77, 0x46, + 0x09, 0x60, 0xdf, 0x19, 0x05, 0x4a, 0x4d, 0xf2, 0x97, 0xdb, 0xb5, 0x94, 0x26, 0x85, 0xab, 0x6c, + 0xac, 0x0c, 0x5d, 0xbf, 0xc3, 0xc1, 0x51, 0xde, 0xb4, 0xf4, 0x96, 0x5d, 0x9e, 0xf2, 0xa5, 0x6e, + 0xca, 0xeb, 0x77, 0x2a, 0x21, 0x5d, 0xbf, 0x53, 0x91, 0x74, 0x76, 0xf5, 0x0f, 0x5a, 0x6b, 0xba, + 0x43, 0x8c, 0x20, 0x7b, 0x28, 0x3b, 0x1a, 0xbe, 0x23, 0xcf, 0xae, 0x5d, 0x48, 0xe9, 0xec, 0xda, + 0x05, 0x4d, 0x37, 0x79, 0xc9, 0x68, 0x8d, 0xe8, 0x86, 0xb2, 0x2e, 0x6f, 0xf2, 0xd2, 0x29, 0xe9, + 0x26, 0x2f, 0x1d, 0x9b, 0xfe, 0x39, 0xf7, 0x1c, 0xd3, 0x23, 0x4a, 0x73, 0x37, 0x9f, 0x03, 0xa4, + 0xe9, 0x9f, 0x03, 0x68, 0x7a, 0x20, 0x8c, 0x76, 0x48, 0x4b, 0x3e, 0x10, 0xc6, 0xbb, 0x21, 0x5a, + 0x82, 0xee, 0x58, 0xb8, 0xc3, 0x84, 0x62, 0xc9, 0x3b, 0x16, 0x0e, 0xa6, 0x3b, 0x96, 0xd0, 0xa5, + 0x42, 0x32, 0xd0, 0x57, 0x6c, 0x79, 0x0d, 0x15, 0x71, 0x74, 0x0d, 0x95, 0x8c, 0xf9, 0x5f, 0x90, + 0xac, 0x67, 0x95, 0xb6, 0xbc, 0xeb, 0x10, 0x50, 0x74, 0xd7, 0x21, 0xda, 0xd9, 0x4e, 0xa3, 0x31, + 0x78, 0x05, 0xd7, 0x3a, 0xc1, 0x3b, 0xce, 0xe7, 0xe4, 0xcf, 0x8c, 0xa0, 0xe9, 0x67, 0x46, 0x40, + 0x12, 0x13, 0x3e, 0x6d, 0x39, 0x29, 0x4c, 0xc2, 0xfb, 0xc1, 0x08, 0x08, 0xcf, 0x23, 0x5c, 0x2b, + 0x2d, 0xcc, 0x57, 0x8c, 0xaa, 0xf8, 0x44, 0xe6, 0xca, 0x37, 0xb0, 0x71, 0x8a, 0xb9, 0x1e, 0x2d, + 0xa1, 0x1c, 0x7e, 0x07, 0x9d, 0xe7, 0x50, 0xee, 0x0d, 0x07, 0x29, 0xd8, 0x8c, 0x60, 0x41, 0xf0, + 0x64, 0xeb, 0x8c, 0x6e, 0xb4, 0x73, 0x3d, 0x5a, 0x57, 0x5e, 0xe9, 0x75, 0xf1, 0xf5, 0xa1, 0xb3, + 0x9b, 0xba, 0x82, 0x45, 0xa2, 0x2b, 0xaf, 0xf4, 0xba, 0xb8, 0xdc, 0x1f, 0xec, 0xa6, 0xae, 0xa0, + 0x13, 0xba, 0xf2, 0xc2, 0x2e, 0x2a, 0x76, 0xc3, 0x97, 0x9a, 0x4d, 0x65, 0x03, 0xaa, 0x7b, 0x7a, + 0x37, 0xd5, 0x95, 0x60, 0xc3, 0xb9, 0x13, 0x47, 0x3a, 0x4b, 0x2f, 0xb5, 0x89, 0x55, 0x93, 0x16, + 0xa0, 0x87, 0xf2, 0x2c, 0x1d, 0x23, 0xa0, 0xb3, 0x74, 0x0c, 0x48, 0x07, 0x94, 0x68, 0x84, 0xad, + 0x6c, 0xca, 0x03, 0x4a, 0xc4, 0xd1, 0x01, 0x25, 0x19, 0x6c, 0x2f, 0xa1, 0x53, 0x4b, 0xeb, 0x9e, + 0xee, 0xef, 0x20, 0x5d, 0xde, 0x95, 0xef, 0x46, 0x1e, 0x99, 0xe2, 0x24, 0xf0, 0xc8, 0x14, 0x07, + 0xd3, 0x31, 0x42, 0xc1, 0xb5, 0x4d, 0xab, 0x31, 0xab, 0x9b, 0xcd, 0x8e, 0x43, 0x94, 0xff, 0x4f, + 0x1e, 0x23, 0x11, 0x34, 0x1d, 0x23, 0x11, 0x10, 0x5d, 0xa0, 0x29, 0xa8, 0xe4, 0xba, 0xe6, 0xaa, + 0xc5, 0xcf, 0x95, 0x9d, 0xa6, 0xa7, 0xfc, 0xff, 0xf2, 0x02, 0x9d, 0x44, 0x43, 0x17, 0xe8, 0x24, + 0x38, 0xdc, 0x3a, 0x25, 0xa4, 0x27, 0x54, 0xfe, 0x52, 0xe4, 0xd6, 0x29, 0x81, 0x06, 0x6e, 0x9d, + 0x92, 0x52, 0x1b, 0xce, 0xa2, 0x02, 0xdb, 0x93, 0xcd, 0x9b, 0xc1, 0x5b, 0xf5, 0x5f, 0x96, 0xd7, + 0xc7, 0x28, 0x9e, 0xae, 0x8f, 0x51, 0x98, 0xcc, 0x87, 0x77, 0xc1, 0x5f, 0x49, 0xe3, 0x13, 0xc8, + 0x3f, 0x56, 0x06, 0xdf, 0x12, 0xf9, 0xf0, 0x91, 0xf2, 0xdd, 0x99, 0x34, 0x46, 0xc1, 0xf0, 0x88, + 0x15, 0x92, 0x19, 0x69, 0xe4, 0x81, 0x49, 0x36, 0x94, 0xcf, 0xa7, 0x32, 0x62, 0x04, 0x32, 0x23, + 0x06, 0xc3, 0x6f, 0xa1, 0xb3, 0x21, 0x6c, 0x81, 0xb4, 0x56, 0x82, 0x99, 0xe9, 0x7b, 0x32, 0xf2, + 0x36, 0x38, 0x99, 0x8c, 0x6e, 0x83, 0x93, 0x31, 0x49, 0xac, 0xb9, 0xe8, 0xfe, 0xea, 0x0e, 0xac, + 0x03, 0x09, 0xa6, 0x30, 0x48, 0x62, 0xcd, 0xa5, 0xf9, 0xbd, 0x3b, 0xb0, 0x0e, 0x64, 0x9a, 0xc2, + 0x00, 0xff, 0x50, 0x06, 0x5d, 0x4a, 0x46, 0x95, 0x9a, 0xcd, 0x59, 0xdb, 0x09, 0x71, 0xca, 0xf7, + 0x65, 0xe4, 0x8b, 0x86, 0xdd, 0x15, 0x9b, 0xeb, 0xd1, 0x76, 0x59, 0x01, 0xfe, 0x24, 0x1a, 0x29, + 0x75, 0x0c, 0xd3, 0x83, 0x87, 0x37, 0xba, 0x71, 0xfe, 0xfe, 0x4c, 0xe4, 0x88, 0x23, 0x62, 0xe1, + 0x88, 0x23, 0x02, 0xf0, 0xeb, 0x68, 0xbc, 0x46, 0x1a, 0x1d, 0xc7, 0xf4, 0x36, 0x35, 0x48, 0x3d, + 0x49, 0x79, 0xfc, 0x40, 0x46, 0x9e, 0xc4, 0x62, 0x14, 0x74, 0x12, 0x8b, 0x01, 0x31, 0x41, 0x13, + 0x33, 0x0f, 0x3d, 0xe2, 0x58, 0x7a, 0x13, 0x2a, 0xa9, 0x79, 0xb6, 0xa3, 0xaf, 0x92, 0x19, 0x4b, + 0x5f, 0x69, 0x12, 0xe5, 0x8b, 0x19, 0x79, 0x5f, 0x95, 0x4e, 0x4a, 0xf7, 0x55, 0xe9, 0x58, 0xbc, + 0x86, 0x1e, 0x4f, 0xc2, 0x96, 0x4d, 0x17, 0xea, 0xf9, 0x52, 0x46, 0xde, 0x58, 0x75, 0xa1, 0xa5, + 0x1b, 0xab, 0x2e, 0x68, 0x08, 0xcf, 0x9d, 0xe4, 0x17, 0xa2, 0xfc, 0x58, 0x46, 0xbe, 0x64, 0x4c, + 0xa4, 0x9a, 0xeb, 0xd1, 0x52, 0xdc, 0x4a, 0xee, 0xa6, 0xf8, 0x54, 0x28, 0x3f, 0xde, 0x9d, 0x6f, + 0xa0, 0xf4, 0x29, 0x2e, 0x19, 0x77, 0x53, 0xfc, 0x11, 0x94, 0x9f, 0xe8, 0xce, 0x37, 0xb4, 0x8b, + 0x48, 0x76, 0x67, 0xa8, 0xa7, 0xdb, 0xf2, 0x2b, 0x3f, 0x99, 0x91, 0xcf, 0xe9, 0x69, 0x84, 0xf4, + 0x9c, 0x9e, 0xea, 0x10, 0xf0, 0x7a, 0x82, 0x45, 0xbd, 0xf2, 0x53, 0x11, 0x2d, 0x8c, 0x51, 0x50, + 0x2d, 0x8c, 0x1b, 0xe2, 0xbf, 0x9e, 0x60, 0x38, 0xae, 0xfc, 0xbd, 0x74, 0x5e, 0x81, 0x50, 0x13, + 0xec, 0xcd, 0x5f, 0x4f, 0xb0, 0x8f, 0x56, 0xfe, 0x7e, 0x3a, 0xaf, 0xf0, 0x79, 0x35, 0x6e, 0x56, + 0x4d, 0x27, 0xa4, 0x8e, 0x67, 0x33, 0xce, 0x92, 0x36, 0xfd, 0x5c, 0x74, 0x42, 0x4a, 0x24, 0x83, + 0x09, 0x29, 0x11, 0x93, 0xc4, 0x9a, 0x7f, 0xf7, 0xcf, 0xef, 0xc0, 0x5a, 0x98, 0x46, 0x13, 0x31, + 0x49, 0xac, 0xb9, 0x18, 0xbe, 0xb2, 0x03, 0x6b, 0x61, 0x1a, 0x4d, 0xc4, 0xe0, 0xcf, 0xa0, 0x73, + 0x21, 0xe6, 0x2e, 0x71, 0xdc, 0xb0, 0xeb, 0x7f, 0x21, 0x23, 0x5f, 0x25, 0xa4, 0xd0, 0xcd, 0xf5, + 0x68, 0x69, 0x2c, 0x12, 0xb9, 0x73, 0xa1, 0xfc, 0xe2, 0x4e, 0xdc, 0xc3, 0x5b, 0x90, 0x14, 0x54, + 0x22, 0x77, 0x2e, 0x97, 0x5f, 0xda, 0x89, 0x7b, 0x78, 0x0d, 0x92, 0x82, 0x9a, 0xea, 0x47, 0xbd, + 0xb0, 0xb7, 0x53, 0x7f, 0x2c, 0x83, 0x86, 0x6b, 0x9e, 0x43, 0xf4, 0x16, 0xf7, 0x4b, 0x9e, 0x40, + 0x03, 0xcc, 0x48, 0xc2, 0xb7, 0x53, 0xd6, 0x82, 0xdf, 0xf8, 0x12, 0x1a, 0x9d, 0xd7, 0x5d, 0x0f, + 0x4a, 0x56, 0x2c, 0x83, 0x3c, 0x04, 0x03, 0xe1, 0x9c, 0x16, 0x81, 0xe2, 0x79, 0x46, 0xc7, 0xca, + 0x41, 0x40, 0x88, 0xdc, 0x8e, 0xee, 0xb8, 0x03, 0xef, 0x6d, 0x15, 0x7b, 0xc0, 0xfb, 0x36, 0x52, + 0x56, 0xfd, 0x7a, 0x06, 0xc5, 0xcc, 0x37, 0xf6, 0xef, 0x3f, 0xb0, 0x84, 0xc6, 0x22, 0x41, 0x48, + 0xb8, 0x95, 0xf3, 0x2e, 0x63, 0x94, 0x44, 0x4b, 0xe3, 0xa7, 0x03, 0xeb, 0xda, 0x3b, 0xda, 0x3c, + 0x77, 0xb5, 0xee, 0xdf, 0xde, 0x2a, 0xe6, 0x3a, 0x4e, 0x53, 0x13, 0x50, 0xdc, 0x15, 0xf0, 0x1f, + 0x16, 0xc2, 0x08, 0x0b, 0xf8, 0x12, 0x77, 0x66, 0xc8, 0x84, 0x0e, 0xda, 0x91, 0x74, 0x1a, 0xcc, + 0x79, 0xe1, 0x93, 0x68, 0xb8, 0xd2, 0x6a, 0x13, 0xc7, 0xb5, 0x2d, 0xdd, 0xb3, 0xfd, 0xb4, 0x7d, + 0xe0, 0xbc, 0x6b, 0x0a, 0x70, 0xd1, 0xa1, 0x54, 0xa4, 0xc7, 0x57, 0xfc, 0x2c, 0xd5, 0x39, 0x88, + 0x6d, 0x71, 0x2a, 0x21, 0x4b, 0xb5, 0x9f, 0x6b, 0xfa, 0x0a, 0xea, 0xbd, 0xe3, 0xea, 0x60, 0x87, + 0x1d, 0x90, 0x76, 0x28, 0x40, 0x24, 0x05, 0x0a, 0x7c, 0x15, 0xf5, 0xc1, 0xb9, 0xd5, 0x55, 0x7a, + 0x81, 0x16, 0xdc, 0xc6, 0x9b, 0x00, 0x11, 0x9d, 0x74, 0x19, 0x0d, 0xbe, 0x8d, 0x0a, 0xe1, 0xa5, + 0x1c, 0x24, 0x9a, 0xf4, 0x63, 0x6c, 0x42, 0x6a, 0x8b, 0xf5, 0x00, 0xc7, 0x32, 0x54, 0x8a, 0x2c, + 0x62, 0x05, 0xf1, 0x1c, 0x1a, 0x0b, 0x61, 0x54, 0x44, 0x7e, 0x6c, 0x5f, 0x48, 0xed, 0x22, 0xf0, + 0xa2, 0xe2, 0x14, 0x59, 0x45, 0x8b, 0xe1, 0x0a, 0xea, 0xf7, 0x7d, 0xc6, 0x07, 0x76, 0x54, 0xd2, + 0x53, 0xdc, 0x67, 0xbc, 0x5f, 0xf4, 0x16, 0xf7, 0xcb, 0xe3, 0x59, 0x34, 0xaa, 0xd9, 0x1d, 0x8f, + 0x2c, 0xdb, 0xfc, 0xce, 0x91, 0x07, 0x7f, 0x84, 0x36, 0x39, 0x14, 0x53, 0xf7, 0x6c, 0x3f, 0x33, + 0x88, 0x98, 0xa1, 0x42, 0x2e, 0x85, 0x17, 0xd1, 0x78, 0xec, 0xfa, 0x52, 0xcc, 0xd7, 0x21, 0x7c, + 0x5e, 0x9c, 0x59, 0xbc, 0x28, 0xfe, 0xfe, 0x0c, 0xea, 0x5b, 0x76, 0x74, 0xd3, 0x73, 0xb9, 0x09, + 0xf7, 0x99, 0xc9, 0x0d, 0x47, 0x6f, 0x53, 0xfd, 0x98, 0x84, 0xe0, 0x25, 0x77, 0xf5, 0x66, 0x87, + 0xb8, 0x53, 0xf7, 0xe8, 0xd7, 0xfd, 0xfb, 0xad, 0xe2, 0x27, 0xf6, 0x90, 0x3d, 0xfc, 0x5a, 0xc0, + 0x89, 0xd5, 0x40, 0x55, 0xc0, 0x83, 0xbf, 0x44, 0x15, 0x60, 0x38, 0xbc, 0x88, 0x10, 0xff, 0xd4, + 0x52, 0xbb, 0xcd, 0xed, 0xc1, 0x05, 0x63, 0x57, 0x1f, 0xc3, 0x14, 0x3b, 0x10, 0x98, 0xde, 0x16, + 0xd3, 0x95, 0x0a, 0x1c, 0xa8, 0x16, 0x2c, 0xf3, 0x16, 0xf9, 0x62, 0x1a, 0x09, 0x25, 0xee, 0x37, + 0x36, 0x41, 0x48, 0xd1, 0x62, 0x78, 0x05, 0x8d, 0x71, 0xbe, 0x41, 0x34, 0xc6, 0x51, 0x79, 0x56, + 0x88, 0xa0, 0x99, 0xd2, 0x06, 0x6d, 0x34, 0x38, 0x58, 0xac, 0x23, 0x52, 0x02, 0x4f, 0x85, 0xc1, + 0xe2, 0x21, 0x37, 0xaa, 0x32, 0x06, 0x1a, 0x7b, 0x7e, 0x7b, 0xab, 0xa8, 0xf8, 0xe5, 0x59, 0x4a, + 0xd5, 0xa4, 0xc4, 0x29, 0x50, 0x44, 0xe4, 0xc1, 0xb4, 0xbe, 0x90, 0xc0, 0x23, 0xaa, 0xf3, 0x72, + 0x11, 0x3c, 0x8d, 0x46, 0x02, 0x73, 0xb4, 0x3b, 0x77, 0x2a, 0x65, 0x30, 0x38, 0xe7, 0x49, 0x40, + 0x23, 0x81, 0x1e, 0x45, 0x26, 0x52, 0x19, 0xc1, 0x33, 0x85, 0x59, 0xa0, 0x47, 0x3c, 0x53, 0xda, + 0x09, 0x9e, 0x29, 0x55, 0xfc, 0x0a, 0x1a, 0x2a, 0xdd, 0xab, 0x71, 0x8f, 0x1b, 0x57, 0x39, 0x15, + 0x46, 0xd8, 0x85, 0xdc, 0x39, 0xdc, 0x3b, 0x47, 0x6c, 0xba, 0x48, 0x8f, 0x67, 0xd0, 0xa8, 0xf4, + 0xa2, 0xe5, 0x2a, 0xa7, 0x81, 0x03, 0x4b, 0x5f, 0x0a, 0x98, 0x3a, 0xcf, 0xa0, 0x2b, 0x25, 0x08, + 0x92, 0x0b, 0x51, 0xad, 0xa1, 0xdb, 0xef, 0x66, 0xd3, 0xde, 0xd0, 0x08, 0x38, 0xf7, 0x80, 0xf9, + 0xfa, 0x00, 0xd3, 0x1a, 0x83, 0xa3, 0xea, 0x0e, 0xc3, 0x49, 0xe9, 0x9b, 0xe4, 0x62, 0xf8, 0x1d, + 0x84, 0x21, 0xbe, 0x29, 0x31, 0xfc, 0x0b, 0x8e, 0x4a, 0xd9, 0x55, 0xce, 0x42, 0x10, 0x27, 0x1c, + 0xf5, 0x2e, 0xab, 0x94, 0xa7, 0x2e, 0xf1, 0xe9, 0xe3, 0xa2, 0xce, 0x4a, 0xd5, 0x83, 0xec, 0xb5, + 0xa6, 0x21, 0xb6, 0x38, 0x81, 0x2b, 0xde, 0x40, 0xe7, 0xaa, 0x0e, 0x79, 0x60, 0xda, 0x1d, 0xd7, + 0x5f, 0x3e, 0xfc, 0x79, 0xeb, 0xdc, 0x8e, 0xf3, 0xd6, 0x93, 0xbc, 0xe2, 0x33, 0x6d, 0x87, 0x3c, + 0xa8, 0xfb, 0xa1, 0x7b, 0xa4, 0x98, 0x17, 0x69, 0xdc, 0xa9, 0xb8, 0xc0, 0xb1, 0x89, 0xc3, 0x4d, + 0xe2, 0x2a, 0x4a, 0x38, 0xd5, 0x32, 0x3f, 0x2d, 0x33, 0xc0, 0x89, 0xe2, 0x8a, 0x14, 0xc3, 0x1a, + 0xc2, 0xb7, 0xa6, 0xfd, 0xcb, 0xae, 0x52, 0xa3, 0x61, 0x77, 0x2c, 0xcf, 0x55, 0x1e, 0x03, 0x66, + 0x2a, 0x15, 0xcb, 0x6a, 0x23, 0x08, 0xe3, 0x55, 0xd7, 0x39, 0x5e, 0x14, 0x4b, 0xbc, 0x34, 0x9e, + 0x47, 0x85, 0xaa, 0x63, 0x3e, 0xd0, 0x3d, 0x72, 0x9b, 0x6c, 0x56, 0xed, 0xa6, 0xd9, 0xd8, 0x04, + 0x2b, 0x7a, 0x3e, 0x55, 0xb6, 0x19, 0xae, 0xbe, 0x4e, 0x36, 0xeb, 0x6d, 0xc0, 0x8a, 0xcb, 0x4a, + 0xb4, 0xa4, 0x18, 0x56, 0xe7, 0xf1, 0xdd, 0x85, 0xd5, 0x21, 0xa8, 0xc0, 0xaf, 0xca, 0x1e, 0x7a, + 0xc4, 0xa2, 0x4b, 0xbd, 0xcb, 0x2d, 0xe6, 0x95, 0xc8, 0xd5, 0x5a, 0x80, 0xe7, 0xa9, 0x9c, 0xd8, + 0x28, 0x23, 0x01, 0x58, 0x6c, 0x58, 0xb4, 0x88, 0xfa, 0xa5, 0x9c, 0x38, 0x75, 0xe2, 0xf3, 0x28, + 0x2f, 0x44, 0x75, 0x85, 0x68, 0x1c, 0x10, 0x01, 0x2b, 0xcf, 0x43, 0xfd, 0x0c, 0xf2, 0x6d, 0x47, + 0xe0, 0x36, 0x06, 0x21, 0xef, 0xfd, 0x50, 0x5b, 0xa6, 0xa1, 0x85, 0x04, 0x10, 0x6e, 0x3c, 0xcc, + 0x78, 0x99, 0x13, 0xc2, 0x8d, 0x87, 0x19, 0x2f, 0xa5, 0x7c, 0x97, 0xd7, 0xd1, 0x10, 0x9f, 0x36, + 0x85, 0x68, 0x34, 0x10, 0x2e, 0xcb, 0x4f, 0x7a, 0xc5, 0xa2, 0x71, 0x09, 0x44, 0xf8, 0x25, 0x48, + 0xfb, 0xe6, 0xbb, 0xe4, 0xf5, 0x86, 0xdb, 0x17, 0x71, 0xe0, 0x47, 0xf2, 0xbe, 0xf9, 0x9e, 0x79, + 0x53, 0x68, 0x44, 0xd4, 0x24, 0x3f, 0xc1, 0x02, 0xcc, 0x79, 0x92, 0xfa, 0x6d, 0x4a, 0x39, 0x8b, + 0xc5, 0x22, 0x78, 0x09, 0x8d, 0xc7, 0x94, 0x87, 0xc7, 0xae, 0x81, 0x74, 0x1b, 0x09, 0x9a, 0x27, + 0xae, 0xa9, 0xb1, 0xb2, 0xea, 0xf7, 0x64, 0x63, 0x2b, 0x06, 0x15, 0x0c, 0xa7, 0x12, 0x3a, 0x07, + 0x04, 0xe3, 0xb3, 0x66, 0x82, 0x11, 0x88, 0xf0, 0x65, 0x34, 0x10, 0xc9, 0xfc, 0x06, 0x4e, 0x9a, + 0x41, 0xda, 0xb7, 0x00, 0x8b, 0xaf, 0xa3, 0x01, 0x3a, 0x7f, 0x5b, 0x91, 0x98, 0x4f, 0x1d, 0x0e, + 0x13, 0x27, 0x5c, 0x9f, 0x8e, 0x96, 0x91, 0xa2, 0x0b, 0xfb, 0x09, 0xba, 0xe2, 0xab, 0x55, 0x18, + 0x3b, 0x3d, 0xd8, 0x2b, 0xf6, 0xee, 0xb4, 0x57, 0x54, 0x7f, 0x33, 0x13, 0xd7, 0x7e, 0x7c, 0x33, + 0x1e, 0xf8, 0x85, 0xe5, 0xe6, 0xf2, 0x81, 0x62, 0xad, 0x41, 0x08, 0x18, 0x29, 0x84, 0x4b, 0x76, + 0xdf, 0x21, 0x5c, 0x72, 0x7b, 0x0c, 0xe1, 0xa2, 0xfe, 0xef, 0x7c, 0x57, 0x83, 0x8b, 0x23, 0x71, + 0x55, 0x7e, 0x91, 0x9e, 0x77, 0x68, 0xed, 0x25, 0x37, 0xb6, 0x6b, 0x67, 0xef, 0xc9, 0x75, 0x9d, + 0x8d, 0x1a, 0x57, 0x93, 0x29, 0xc5, 0x4c, 0xe9, 0x10, 0x1a, 0x28, 0x9f, 0x90, 0x29, 0x3d, 0x9a, + 0x5e, 0x4d, 0x2c, 0x80, 0x3f, 0x86, 0x06, 0xc3, 0x9c, 0xef, 0xbd, 0x42, 0xa0, 0xa9, 0x84, 0x54, + 0xef, 0x21, 0x25, 0xfe, 0x2c, 0xea, 0x93, 0xf2, 0xfb, 0x5d, 0xdb, 0x85, 0x85, 0xca, 0xa4, 0x18, + 0xbe, 0x90, 0x9d, 0x1d, 0xa2, 0xb9, 0xfd, 0x38, 0x53, 0xbc, 0x8c, 0x4e, 0x55, 0x1d, 0x62, 0x80, + 0x2d, 0xd4, 0xcc, 0xc3, 0xb6, 0xc3, 0x83, 0x4b, 0xb2, 0x01, 0x0c, 0x4b, 0x47, 0xdb, 0x47, 0xd3, + 0x45, 0x8d, 0xe3, 0x05, 0x46, 0x49, 0xc5, 0xe9, 0x7e, 0x82, 0xb5, 0xe4, 0x36, 0xd9, 0xdc, 0xb0, + 0x1d, 0x83, 0xc5, 0x5f, 0xe4, 0xfb, 0x09, 0x2e, 0xe8, 0x75, 0x8e, 0x12, 0xf7, 0x13, 0x72, 0xa1, + 0x89, 0x17, 0xd1, 0xd0, 0x7e, 0x43, 0x00, 0xfe, 0x62, 0x36, 0xc5, 0x74, 0xf1, 0xd1, 0x4d, 0xdd, + 0x10, 0xa4, 0xd1, 0xe9, 0x4d, 0x49, 0xa3, 0xf3, 0xad, 0x6c, 0x8a, 0x5d, 0xe6, 0x23, 0x9d, 0xee, + 0x22, 0x10, 0x86, 0x9c, 0xee, 0x22, 0xcc, 0x34, 0x62, 0x1a, 0x9a, 0x48, 0x14, 0x49, 0x8c, 0xd3, + 0xb7, 0x63, 0x62, 0x9c, 0x9f, 0xce, 0x75, 0xb3, 0x5b, 0x3d, 0x91, 0xfd, 0x5e, 0x64, 0x7f, 0x1d, + 0x0d, 0x05, 0x92, 0xe5, 0x49, 0x92, 0x47, 0x82, 0x80, 0xa3, 0x0c, 0x0c, 0x65, 0x04, 0x22, 0x7c, + 0x85, 0xb5, 0xb5, 0x66, 0xbe, 0xcb, 0x82, 0xee, 0x8d, 0xf0, 0x70, 0x6a, 0xba, 0xa7, 0xd7, 0x5d, + 0xf3, 0x5d, 0xa2, 0x05, 0x68, 0xf5, 0x9f, 0x66, 0x13, 0x8d, 0x7f, 0x4f, 0xfa, 0x68, 0x0f, 0x7d, + 0x94, 0x20, 0x44, 0x66, 0xb6, 0x7c, 0x22, 0xc4, 0x3d, 0x08, 0xf1, 0x4f, 0xb2, 0x89, 0x46, 0xde, + 0x27, 0x42, 0xdc, 0xcb, 0x6c, 0x71, 0x15, 0x0d, 0x6a, 0xf6, 0x86, 0x3b, 0x0d, 0x67, 0x16, 0x36, + 0x57, 0xc0, 0x44, 0xed, 0xd8, 0x1b, 0x6e, 0x1d, 0x4e, 0x23, 0x5a, 0x48, 0xa0, 0x7e, 0x3b, 0xdb, + 0xc5, 0x0c, 0xfe, 0x44, 0xf0, 0xef, 0xe7, 0x12, 0xf9, 0x2b, 0x59, 0xc9, 0xcc, 0xfe, 0x91, 0xce, + 0x1b, 0x57, 0x6b, 0xac, 0x91, 0x96, 0x1e, 0xcd, 0x1b, 0xe7, 0x02, 0x94, 0xa7, 0x9d, 0x09, 0x49, + 0xd4, 0xaf, 0x66, 0x23, 0x7e, 0x06, 0x27, 0xb2, 0xdb, 0xb5, 0xec, 0x02, 0xad, 0xe3, 0xae, 0x13, + 0x27, 0x92, 0xdb, 0xad, 0xe4, 0x7e, 0x30, 0x1b, 0xf1, 0x32, 0x79, 0x74, 0x53, 0x48, 0x7d, 0x35, + 0x1b, 0xf7, 0x98, 0x79, 0x74, 0x35, 0xe9, 0x2a, 0x1a, 0xe4, 0x72, 0x08, 0x96, 0x0a, 0x36, 0xef, + 0x33, 0x20, 0x5c, 0xa0, 0x06, 0x04, 0xea, 0xf7, 0x65, 0x91, 0xec, 0xfd, 0xf3, 0x88, 0xea, 0xd0, + 0xaf, 0x64, 0x65, 0xbf, 0xa7, 0x47, 0x57, 0x7f, 0x26, 0x11, 0xaa, 0x75, 0x56, 0x1a, 0x3c, 0x6c, + 0x56, 0xaf, 0x70, 0x03, 0x1f, 0x40, 0x35, 0x81, 0x42, 0xfd, 0x3f, 0xd9, 0x44, 0x67, 0xac, 0x47, + 0x57, 0x80, 0x37, 0xe0, 0x56, 0xbc, 0x61, 0x85, 0x13, 0x39, 0x5c, 0x42, 0xd2, 0xf1, 0x17, 0x8b, + 0x76, 0xef, 0x13, 0xe2, 0x8f, 0x27, 0x6c, 0xd7, 0x20, 0x96, 0x60, 0x62, 0x0a, 0x6d, 0x71, 0xe3, + 0xf6, 0x2f, 0xb2, 0x3b, 0xf9, 0xae, 0x3d, 0xca, 0xab, 0x6a, 0x7f, 0x55, 0xdf, 0x84, 0x18, 0x2b, + 0xb4, 0x27, 0x86, 0x59, 0x2c, 0xf6, 0x36, 0x03, 0x89, 0x2f, 0x62, 0x9c, 0x4a, 0xfd, 0xe3, 0xde, + 0x64, 0xc7, 0xa9, 0x47, 0x57, 0x84, 0xe7, 0x51, 0xbe, 0xaa, 0x7b, 0x6b, 0x5c, 0x93, 0xe1, 0xb5, + 0xae, 0xad, 0x7b, 0x6b, 0x1a, 0x40, 0xf1, 0x15, 0x34, 0xa0, 0xe9, 0x1b, 0x62, 0xea, 0x70, 0xb8, + 0xd8, 0x71, 0xf4, 0x0d, 0x9e, 0x3f, 0x3e, 0x40, 0x63, 0x35, 0xc8, 0xd3, 0xc0, 0x6e, 0xbe, 0x21, + 0xc8, 0x39, 0xcb, 0xd3, 0x10, 0x64, 0x67, 0x38, 0x8f, 0xf2, 0x53, 0xb6, 0xb1, 0x09, 0xc6, 0x2c, + 0xc3, 0xac, 0xb2, 0x15, 0xdb, 0xd8, 0xd4, 0x00, 0x8a, 0x7f, 0x28, 0x83, 0xfa, 0xe7, 0x88, 0x6e, + 0xd0, 0x11, 0x32, 0xd8, 0xcd, 0x16, 0xe4, 0xcd, 0xc3, 0xb1, 0x05, 0x19, 0x5f, 0x63, 0x95, 0x89, + 0x8a, 0xc2, 0xeb, 0xc7, 0xb7, 0xd0, 0xc0, 0xb4, 0xee, 0x91, 0x55, 0xdb, 0xd9, 0x04, 0xeb, 0x96, + 0xd1, 0xd0, 0x7a, 0x54, 0xd2, 0x1f, 0x9f, 0x88, 0xbd, 0x8c, 0x35, 0xf8, 0x2f, 0x2d, 0x28, 0x4c, + 0xc5, 0xc2, 0xf3, 0xb7, 0x0d, 0x85, 0x62, 0x61, 0x89, 0xda, 0x82, 0x34, 0x6d, 0xc1, 0xb5, 0xf2, + 0x70, 0xf2, 0xb5, 0x32, 0xec, 0x1e, 0xc1, 0x02, 0x0e, 0xb2, 0x23, 0x8c, 0xc0, 0xa2, 0xcf, 0x76, + 0x8f, 0x00, 0x85, 0xe4, 0x08, 0x9a, 0x40, 0xa2, 0x7e, 0xa3, 0x17, 0x25, 0xba, 0x59, 0x9c, 0x28, + 0xf9, 0x89, 0x92, 0x87, 0x4a, 0x5e, 0x8e, 0x29, 0xf9, 0x44, 0xdc, 0x71, 0xe7, 0x03, 0xaa, 0xe1, + 0x3f, 0x92, 0x8f, 0xb9, 0xfd, 0x3d, 0xda, 0xa7, 0xcb, 0x50, 0x7a, 0xbd, 0x3b, 0x4a, 0x2f, 0x18, + 0x10, 0x7d, 0x3b, 0x0e, 0x88, 0xfe, 0xdd, 0x0e, 0x88, 0x81, 0xd4, 0x01, 0x11, 0x2a, 0xc8, 0x60, + 0xaa, 0x82, 0x54, 0xf8, 0xa0, 0x41, 0xdd, 0x33, 0xef, 0x9c, 0xdf, 0xde, 0x2a, 0x8e, 0xd2, 0xd1, + 0x94, 0x98, 0x73, 0x07, 0x58, 0xa8, 0x5f, 0xcf, 0x77, 0xf1, 0xd5, 0x3d, 0x12, 0x1d, 0xb9, 0x81, + 0x72, 0xa5, 0x76, 0x9b, 0xeb, 0xc7, 0x29, 0xc1, 0x4d, 0x38, 0xa5, 0x14, 0xa5, 0xc6, 0x2f, 0xa1, + 0x5c, 0xe9, 0x5e, 0x2d, 0x1a, 0x71, 0xb8, 0x74, 0xaf, 0xc6, 0xbf, 0x24, 0xb5, 0xec, 0xbd, 0x1a, + 0x7e, 0x39, 0x0c, 0xfd, 0xb3, 0xd6, 0xb1, 0xd6, 0xf9, 0x41, 0x91, 0x1b, 0xc1, 0xfa, 0x96, 0x36, + 0x0d, 0x8a, 0xa2, 0xc7, 0xc5, 0x08, 0x6d, 0x44, 0x9b, 0xfa, 0x76, 0xaf, 0x4d, 0xfd, 0x3b, 0x6a, + 0xd3, 0xc0, 0x6e, 0xb5, 0x69, 0x70, 0x17, 0xda, 0x84, 0x76, 0xd4, 0xa6, 0xa1, 0x83, 0x6b, 0x53, + 0x1b, 0x4d, 0xc4, 0xe3, 0x2b, 0x04, 0x1a, 0xa1, 0x21, 0x1c, 0xc7, 0x72, 0xc3, 0x12, 0x78, 0xfa, + 0xef, 0x30, 0x6c, 0x9d, 0xe5, 0x59, 0x8c, 0x66, 0x29, 0xd4, 0x12, 0x4a, 0xab, 0xbf, 0x98, 0x4d, + 0x0f, 0x0b, 0x71, 0x3c, 0xa7, 0xb8, 0xef, 0x4a, 0x94, 0x52, 0x5e, 0x76, 0x88, 0x4a, 0x97, 0x72, + 0x84, 0x6d, 0x92, 0xcc, 0xbe, 0x96, 0x49, 0x8b, 0x55, 0x71, 0x20, 0x89, 0x7d, 0x38, 0x6e, 0xac, + 0x06, 0xd6, 0xf3, 0xae, 0x6c, 0xa5, 0x16, 0x4d, 0xdb, 0x97, 0xdb, 0x67, 0xda, 0xbe, 0xdf, 0xcc, + 0xa0, 0x53, 0xb7, 0x3b, 0x2b, 0x84, 0x1b, 0xa7, 0x05, 0xcd, 0x78, 0x07, 0x21, 0x0a, 0xe6, 0x46, + 0x2c, 0x19, 0x30, 0x62, 0xf9, 0x88, 0x18, 0x67, 0x22, 0x52, 0x60, 0x32, 0xa4, 0x66, 0x06, 0x2c, + 0x17, 0x7c, 0x13, 0xcb, 0xf5, 0xce, 0x0a, 0xa9, 0xc7, 0x2c, 0x59, 0x04, 0xee, 0x13, 0xaf, 0x30, + 0xe3, 0xf5, 0xfd, 0x1a, 0x8d, 0xfc, 0x7c, 0x36, 0x35, 0xb4, 0xc7, 0xb1, 0xcd, 0xac, 0xf0, 0xe9, + 0xc4, 0x5e, 0x89, 0x66, 0x58, 0x48, 0x20, 0x89, 0x70, 0x4c, 0xe2, 0x92, 0x2c, 0xb0, 0x63, 0x9e, + 0xef, 0xe3, 0x7d, 0x15, 0xd8, 0xef, 0x65, 0x52, 0x43, 0xb0, 0x1c, 0x57, 0x81, 0xa9, 0xbf, 0x9d, + 0xf5, 0x23, 0xbf, 0x1c, 0xe8, 0x13, 0xae, 0xa2, 0x41, 0x1e, 0xde, 0x5e, 0xb6, 0xad, 0xe5, 0x57, + 0x79, 0x70, 0x35, 0x1c, 0x10, 0xd0, 0x65, 0xde, 0x8f, 0x4c, 0x11, 0x24, 0x7a, 0x84, 0x65, 0xde, + 0xe4, 0x50, 0x4a, 0x2f, 0x90, 0xd0, 0x85, 0x7c, 0xe6, 0xa1, 0xe9, 0xc1, 0xae, 0x80, 0xf6, 0x65, + 0x8e, 0x2d, 0xe4, 0xe4, 0xa1, 0xe9, 0xb1, 0x3d, 0x41, 0x80, 0xa6, 0x8b, 0x74, 0x2d, 0xcc, 0x66, + 0xc6, 0x17, 0x69, 0x97, 0x27, 0x75, 0xe3, 0xce, 0x5c, 0x57, 0xd1, 0x20, 0x37, 0x58, 0xe5, 0x66, + 0x26, 0xbc, 0xb5, 0xdc, 0xc4, 0x15, 0x5a, 0x1b, 0x10, 0x50, 0x8e, 0x1a, 0x59, 0x0d, 0x0d, 0xeb, + 0x80, 0xa3, 0x03, 0x10, 0x8d, 0x63, 0xd4, 0xed, 0x6c, 0x3c, 0x00, 0xcd, 0xa3, 0x7b, 0x28, 0xb8, + 0x22, 0x1b, 0xab, 0x81, 0x85, 0x26, 0x6c, 0xb8, 0x44, 0x5b, 0x59, 0xb6, 0xef, 0xba, 0x8e, 0x06, + 0x6e, 0x93, 0x4d, 0x66, 0x57, 0xd9, 0x17, 0x9a, 0xe2, 0xae, 0x73, 0x98, 0x78, 0xa3, 0xe9, 0xd3, + 0xa9, 0xbf, 0x91, 0x8d, 0x87, 0xd6, 0x79, 0x74, 0x85, 0xfd, 0x51, 0xd4, 0x0f, 0xa2, 0xac, 0xf8, + 0x57, 0xea, 0x20, 0x40, 0x10, 0xb7, 0x6c, 0xe1, 0xeb, 0x93, 0xa9, 0x3f, 0xde, 0x17, 0x8d, 0xb7, + 0xf4, 0xe8, 0x4a, 0xef, 0x13, 0x68, 0x68, 0xda, 0xb6, 0x5c, 0xd3, 0xf5, 0x88, 0xd5, 0xf0, 0x15, + 0xf6, 0x31, 0xba, 0x61, 0x69, 0x84, 0x60, 0xd1, 0xf3, 0x46, 0xa0, 0xde, 0x8f, 0xf2, 0xe2, 0xe7, + 0xd1, 0x20, 0x88, 0x1c, 0xec, 0x90, 0x85, 0x34, 0xb1, 0x2b, 0x14, 0x18, 0x35, 0x42, 0x0e, 0x49, + 0xf1, 0x1d, 0x34, 0x30, 0xbd, 0x66, 0x36, 0x0d, 0x87, 0x58, 0x3c, 0x1f, 0xfa, 0x93, 0xc9, 0xd1, + 0xb1, 0x26, 0xe1, 0x5f, 0xa0, 0x65, 0xcd, 0x69, 0xf0, 0x62, 0x92, 0xef, 0x11, 0x87, 0x4d, 0xfc, + 0xcd, 0x2c, 0x42, 0x61, 0x01, 0xfc, 0x04, 0xca, 0x06, 0x89, 0x78, 0xc0, 0x0c, 0x44, 0xd2, 0xa0, + 0x2c, 0x4c, 0xc5, 0x7c, 0x6c, 0x67, 0x77, 0x1c, 0xdb, 0x77, 0x50, 0x1f, 0xbb, 0x51, 0x02, 0x4b, + 0x6d, 0x21, 0x04, 0x4c, 0x6a, 0x83, 0x27, 0x81, 0x9e, 0x1d, 0x16, 0x61, 0x67, 0x27, 0x59, 0x3d, + 0x33, 0x66, 0x13, 0x0d, 0xd4, 0x0b, 0x7f, 0xe1, 0x4b, 0x28, 0x0f, 0x52, 0xcc, 0xc0, 0x39, 0x11, + 0xdc, 0x44, 0x23, 0xf2, 0x03, 0x3c, 0xed, 0xa6, 0x69, 0xdb, 0xf2, 0x68, 0xd5, 0xd0, 0xea, 0x61, + 0x2e, 0x17, 0x0e, 0x93, 0xe4, 0xc2, 0x61, 0xea, 0x3f, 0xcf, 0x26, 0x44, 0x02, 0x7b, 0x74, 0x87, + 0xc9, 0x8b, 0x08, 0x81, 0x23, 0x33, 0x95, 0xa7, 0xef, 0x02, 0x01, 0xa3, 0x04, 0x18, 0x81, 0xda, + 0x4a, 0xdb, 0xfa, 0x90, 0x58, 0xfd, 0x9d, 0x4c, 0x2c, 0x7c, 0xd4, 0x81, 0xe4, 0x28, 0xee, 0x7a, + 0xb2, 0xfb, 0xdc, 0x26, 0xfa, 0x7d, 0x91, 0xdb, 0x5b, 0x5f, 0xc8, 0xdf, 0x72, 0x08, 0x3b, 0xbf, + 0xa3, 0xfc, 0x96, 0x6f, 0x64, 0x93, 0x82, 0x69, 0x1d, 0x4f, 0x15, 0x0f, 0x73, 0x8d, 0xe7, 0xf7, + 0x90, 0x6b, 0xfc, 0x6d, 0x34, 0x16, 0x09, 0x31, 0xc5, 0xb3, 0x63, 0x5d, 0xea, 0x1e, 0xab, 0x2a, + 0xdd, 0x05, 0x5e, 0x22, 0x53, 0xff, 0x6f, 0xa6, 0x7b, 0x80, 0xb1, 0x23, 0x57, 0x9d, 0x04, 0x01, + 0xe4, 0xfe, 0x62, 0x04, 0x70, 0x08, 0xc7, 0xcc, 0xe3, 0x2d, 0x80, 0x0f, 0xc8, 0xe4, 0xf1, 0x7e, + 0x0b, 0xe0, 0xc7, 0x33, 0x3b, 0xc6, 0x87, 0x3b, 0x6a, 0x19, 0xa8, 0xff, 0x31, 0x93, 0x18, 0xc7, + 0xed, 0x40, 0xed, 0x7a, 0x19, 0xf5, 0x31, 0xb3, 0x15, 0xde, 0x2a, 0x21, 0xf2, 0x3d, 0x85, 0xa6, + 0x94, 0xe7, 0x65, 0xf0, 0x3c, 0xea, 0x67, 0x6d, 0x30, 0xa2, 0x19, 0x22, 0x13, 0xda, 0x69, 0xa4, + 0x4d, 0x8e, 0x1c, 0xad, 0xfe, 0x56, 0x26, 0x16, 0x56, 0xee, 0x08, 0xbf, 0x2d, 0x9c, 0xaa, 0x73, + 0xbb, 0x9f, 0xaa, 0xd5, 0x3f, 0xca, 0x26, 0x47, 0xb5, 0x3b, 0xc2, 0x0f, 0x39, 0x8c, 0xeb, 0xaa, + 0xfd, 0xad, 0x5b, 0xcb, 0x68, 0x54, 0x96, 0x05, 0x5f, 0xb6, 0x2e, 0x26, 0xc7, 0xf6, 0x4b, 0x69, + 0x45, 0x84, 0x87, 0xfa, 0x5e, 0x26, 0x1e, 0x90, 0xef, 0xc8, 0xe7, 0xa7, 0xfd, 0x69, 0x8b, 0xfc, + 0x29, 0x1f, 0x90, 0xb5, 0xe6, 0x30, 0x3e, 0xe5, 0x03, 0xb2, 0x6a, 0xec, 0xef, 0x53, 0x7e, 0x26, + 0x9b, 0x16, 0xcf, 0xf0, 0xc8, 0x3f, 0xe8, 0x53, 0xa2, 0x90, 0x59, 0xcb, 0xf8, 0xa7, 0x3d, 0x91, + 0x16, 0x40, 0x30, 0x85, 0x67, 0x8c, 0xcf, 0xfe, 0xc6, 0x78, 0xa2, 0xb0, 0x3e, 0x20, 0x8a, 0x7c, + 0x3c, 0x84, 0xf5, 0x01, 0x19, 0x2a, 0x1f, 0x3c, 0x61, 0xfd, 0x5a, 0x76, 0xb7, 0x41, 0x34, 0x4f, + 0x84, 0x17, 0x13, 0xde, 0x97, 0xb3, 0xf1, 0xe0, 0xae, 0x47, 0x2e, 0xa6, 0x59, 0xd4, 0xc7, 0xc3, + 0xcc, 0xa6, 0x0a, 0x87, 0xe1, 0xd3, 0x76, 0x34, 0xfc, 0x3b, 0x6e, 0x22, 0xfe, 0x50, 0xb2, 0x3b, + 0x91, 0x30, 0x5a, 0xf5, 0xdb, 0x99, 0x48, 0x24, 0xd4, 0x23, 0xb9, 0x42, 0xd8, 0xd7, 0x92, 0x84, + 0x5f, 0xf1, 0x2f, 0x33, 0xf3, 0x91, 0x24, 0x93, 0xc1, 0xf7, 0x94, 0x89, 0xa7, 0x9b, 0xcd, 0x68, + 0x79, 0xee, 0x73, 0xff, 0x1b, 0x59, 0x34, 0x1e, 0x23, 0xc5, 0x97, 0xa4, 0x28, 0x34, 0x70, 0x2d, + 0x19, 0x31, 0xce, 0x66, 0xf1, 0x68, 0xf6, 0x70, 0x93, 0x7a, 0x09, 0xe5, 0xcb, 0xfa, 0x26, 0xfb, + 0xb6, 0x5e, 0xc6, 0xd2, 0xd0, 0x37, 0xc5, 0x1b, 0x37, 0xc0, 0xe3, 0x15, 0x74, 0x86, 0xbd, 0x87, + 0x98, 0xb6, 0xb5, 0x6c, 0xb6, 0x48, 0xc5, 0x5a, 0x30, 0x9b, 0x4d, 0xd3, 0xe5, 0x8f, 0x66, 0x57, + 0xb7, 0xb7, 0x8a, 0x97, 0x3d, 0xdb, 0xd3, 0x9b, 0x75, 0xe2, 0x93, 0xd5, 0x3d, 0xb3, 0x45, 0xea, + 0xa6, 0x55, 0x6f, 0x01, 0xa5, 0xc0, 0x32, 0x99, 0x15, 0xae, 0xb0, 0x2c, 0x98, 0xb5, 0x86, 0x6e, + 0x59, 0xc4, 0xa8, 0x58, 0x53, 0x9b, 0x1e, 0x61, 0x8f, 0x6d, 0x39, 0x76, 0x25, 0xc8, 0x7c, 0xaf, + 0x19, 0x9a, 0x32, 0x5e, 0xa1, 0x04, 0x5a, 0x42, 0x21, 0xf5, 0xd7, 0xf3, 0x09, 0x41, 0x70, 0x8f, + 0x91, 0xfa, 0xf8, 0x3d, 0x9d, 0xdf, 0xa1, 0xa7, 0xaf, 0xa1, 0x7e, 0x1e, 0x67, 0x92, 0x3f, 0x30, + 0x80, 0xb1, 0xf8, 0x03, 0x06, 0x12, 0x5f, 0x68, 0x38, 0x15, 0x6e, 0xa2, 0x89, 0x65, 0xda, 0x4d, + 0xc9, 0x9d, 0xd9, 0xb7, 0x8f, 0xce, 0xec, 0xc2, 0x0f, 0xbf, 0x85, 0xce, 0x01, 0x36, 0xa1, 0x5b, + 0xfb, 0xa1, 0x2a, 0x88, 0xcc, 0xc4, 0xaa, 0x4a, 0xee, 0xdc, 0xb4, 0xf2, 0xf8, 0x53, 0x68, 0x38, + 0x18, 0x20, 0x26, 0x71, 0xf9, 0xcb, 0x45, 0x97, 0x71, 0xc6, 0xc2, 0x9e, 0x51, 0x30, 0x98, 0x68, + 0xc9, 0xa1, 0xb3, 0x24, 0x5e, 0xea, 0x7f, 0xc8, 0x74, 0x0b, 0x7b, 0x7c, 0xe4, 0xb3, 0xf2, 0x2b, + 0xa8, 0xdf, 0x60, 0x1f, 0xc5, 0x75, 0xaa, 0x7b, 0x60, 0x64, 0x46, 0xaa, 0xf9, 0x65, 0xd4, 0x3f, + 0xcc, 0x74, 0x8d, 0xb6, 0x7c, 0xdc, 0x3f, 0xef, 0xcb, 0xb9, 0x94, 0xcf, 0xe3, 0x93, 0xe8, 0x15, + 0x54, 0x30, 0xc3, 0x48, 0xbe, 0xf5, 0x30, 0xbc, 0x93, 0x36, 0x26, 0xc0, 0x61, 0x74, 0xdd, 0x44, + 0x67, 0x7d, 0xc3, 0x42, 0xc7, 0xb7, 0xc0, 0x72, 0xeb, 0x1d, 0xc7, 0x64, 0xe3, 0x52, 0x3b, 0xed, + 0x46, 0xcc, 0xb3, 0xdc, 0x3b, 0x8e, 0x49, 0x2b, 0xd0, 0xbd, 0x35, 0x62, 0xe9, 0xf5, 0x0d, 0xdb, + 0x59, 0x87, 0xd8, 0x9a, 0x6c, 0x70, 0x6a, 0x63, 0x0c, 0x7e, 0xcf, 0x07, 0xe3, 0xa7, 0xd0, 0xc8, + 0x6a, 0xb3, 0x43, 0x82, 0x68, 0x86, 0xec, 0xad, 0x4f, 0x1b, 0xa6, 0xc0, 0xe0, 0x85, 0xe4, 0x02, + 0x42, 0x40, 0xe4, 0x41, 0x2c, 0x6c, 0x78, 0xd8, 0xd3, 0x06, 0x29, 0x64, 0x99, 0x77, 0xd7, 0x04, + 0xd3, 0x6a, 0x26, 0xa4, 0x7a, 0xd3, 0xb6, 0x56, 0xeb, 0x1e, 0x71, 0x5a, 0xd0, 0x50, 0x30, 0x4e, + 0xd4, 0xce, 0x02, 0x05, 0x3c, 0x9d, 0xb8, 0xf3, 0xb6, 0xb5, 0xba, 0x4c, 0x9c, 0x16, 0x6d, 0xea, + 0x55, 0x84, 0x79, 0x53, 0x1d, 0xb8, 0xf4, 0x60, 0x1f, 0x07, 0x76, 0x8a, 0x1a, 0xff, 0x08, 0x76, + 0x1b, 0x02, 0x1f, 0x56, 0x44, 0x43, 0x2c, 0xa4, 0x1b, 0x13, 0x1a, 0x98, 0x2a, 0x6a, 0x88, 0x81, + 0x40, 0x5e, 0x67, 0x11, 0xb7, 0x5e, 0x60, 0x56, 0xd3, 0x1a, 0xff, 0xa5, 0xfe, 0x6e, 0x36, 0x2d, + 0x50, 0xf2, 0x71, 0x7d, 0xe3, 0xc0, 0x73, 0x08, 0xf1, 0x8c, 0x92, 0xf4, 0x73, 0x23, 0x06, 0xad, + 0x21, 0x26, 0x85, 0x87, 0x50, 0x56, 0x58, 0x20, 0x7a, 0xf7, 0xb0, 0x5d, 0x4c, 0x12, 0xe9, 0x21, + 0x9c, 0xe2, 0xc2, 0xc6, 0x64, 0xf7, 0xb0, 0x5a, 0x1d, 0xbd, 0x10, 0x45, 0x55, 0xe8, 0xdd, 0xe7, + 0x6d, 0x6d, 0x92, 0x48, 0x8f, 0xf7, 0x4b, 0xdc, 0x91, 0x6b, 0xe9, 0xef, 0x65, 0x53, 0x03, 0x82, + 0x9f, 0xc8, 0xf4, 0x30, 0x65, 0x7a, 0x32, 0xf4, 0x0f, 0x34, 0xf4, 0x13, 0x65, 0x7a, 0x32, 0xf6, + 0x0f, 0xa2, 0xa7, 0xcf, 0x2c, 0xb0, 0xf0, 0x99, 0xb7, 0x4d, 0xcb, 0xc0, 0x8f, 0xa1, 0x33, 0x77, + 0x6a, 0x33, 0x5a, 0xfd, 0x76, 0x65, 0xb1, 0x5c, 0xbf, 0xb3, 0x58, 0xab, 0xce, 0x4c, 0x57, 0x66, + 0x2b, 0x33, 0xe5, 0x42, 0x0f, 0x3e, 0x85, 0xc6, 0x42, 0xd4, 0xdc, 0x9d, 0x85, 0xd2, 0x62, 0x21, + 0x83, 0xc7, 0xd1, 0x48, 0x08, 0x9c, 0x5a, 0x5a, 0x2e, 0x64, 0x9f, 0x79, 0x1a, 0x0d, 0xc1, 0xfe, + 0xa5, 0xc4, 0xda, 0x34, 0x8c, 0x06, 0x96, 0xa6, 0x6a, 0x33, 0xda, 0x5d, 0x60, 0x82, 0x50, 0x5f, + 0x79, 0x66, 0x91, 0x32, 0xcc, 0x3c, 0xf3, 0xbf, 0x32, 0x08, 0xd5, 0x66, 0x97, 0xab, 0x9c, 0x70, + 0x08, 0xf5, 0x57, 0x16, 0xef, 0x96, 0xe6, 0x2b, 0x94, 0x6e, 0x00, 0xe5, 0x97, 0xaa, 0x33, 0xb4, + 0x86, 0x41, 0xd4, 0x3b, 0x3d, 0xbf, 0x54, 0x9b, 0x29, 0x64, 0x29, 0x50, 0x9b, 0x29, 0x95, 0x0b, + 0x39, 0x0a, 0xbc, 0xa7, 0x55, 0x96, 0x67, 0x0a, 0x79, 0xfa, 0xe7, 0x7c, 0x6d, 0xb9, 0xb4, 0x5c, + 0xe8, 0xa5, 0x7f, 0xce, 0xc2, 0x9f, 0x7d, 0x94, 0x59, 0x6d, 0x66, 0x19, 0x7e, 0xf4, 0xd3, 0x26, + 0xcc, 0xfa, 0xbf, 0x06, 0x28, 0x8a, 0xb2, 0x2e, 0x57, 0xb4, 0xc2, 0x20, 0xfd, 0x41, 0x59, 0xd2, + 0x1f, 0x88, 0x36, 0x4e, 0x9b, 0x59, 0x58, 0xba, 0x3b, 0x53, 0x18, 0xa2, 0xbc, 0x16, 0x6e, 0x53, + 0xf0, 0x30, 0xfd, 0x53, 0x5b, 0xa0, 0x7f, 0x8e, 0x50, 0x4e, 0xda, 0x4c, 0x69, 0xbe, 0x5a, 0x5a, + 0x9e, 0x2b, 0x8c, 0xd2, 0xf6, 0x00, 0xcf, 0x31, 0x56, 0x72, 0xb1, 0xb4, 0x30, 0x53, 0x28, 0x70, + 0x9a, 0xf2, 0x7c, 0x65, 0xf1, 0x76, 0x61, 0x1c, 0x1a, 0xf2, 0xd6, 0x02, 0xfc, 0xc0, 0xb4, 0x00, + 0xfc, 0x75, 0xea, 0x99, 0xcf, 0xa0, 0xbe, 0xa5, 0x1a, 0x58, 0x26, 0x9d, 0x43, 0xa7, 0x96, 0x6a, + 0xf5, 0xe5, 0xb7, 0xaa, 0x33, 0x11, 0x79, 0x8f, 0xa3, 0x11, 0x1f, 0x31, 0x5f, 0x59, 0xbc, 0xf3, + 0x26, 0x93, 0xb6, 0x0f, 0x5a, 0x28, 0x4d, 0x2f, 0xd5, 0x0a, 0x59, 0xda, 0x2b, 0x3e, 0xe8, 0x5e, + 0x65, 0xb1, 0xbc, 0x74, 0xaf, 0x56, 0xc8, 0x3d, 0xf3, 0xc0, 0xcf, 0x68, 0xb5, 0xe4, 0x98, 0xab, + 0xa6, 0x85, 0x2f, 0xa0, 0xc7, 0xca, 0x33, 0x77, 0x2b, 0xd3, 0x33, 0xf5, 0x25, 0xad, 0x72, 0xab, + 0xb2, 0x18, 0xa9, 0xe9, 0x0c, 0x1a, 0x97, 0xd1, 0xa5, 0x6a, 0xa5, 0x90, 0xc1, 0x67, 0x11, 0x96, + 0xc1, 0xaf, 0x97, 0x16, 0x66, 0x0b, 0x59, 0xac, 0xa0, 0xd3, 0x32, 0xbc, 0xb2, 0xb8, 0x7c, 0x67, + 0x71, 0xa6, 0x90, 0x7b, 0xe6, 0x27, 0x33, 0xe8, 0x4c, 0xa2, 0x83, 0x2a, 0x56, 0xd1, 0xc5, 0x99, + 0xf9, 0x52, 0x6d, 0xb9, 0x32, 0x5d, 0x9b, 0x29, 0x69, 0xd3, 0x73, 0xf5, 0xe9, 0xd2, 0xf2, 0xcc, + 0xad, 0x25, 0xed, 0xad, 0xfa, 0xad, 0x99, 0xc5, 0x19, 0xad, 0x34, 0x5f, 0xe8, 0xc1, 0x4f, 0xa1, + 0x62, 0x0a, 0x4d, 0x6d, 0x66, 0xfa, 0x8e, 0x56, 0x59, 0x7e, 0xab, 0x90, 0xc1, 0x4f, 0xa2, 0x0b, + 0xa9, 0x44, 0xf4, 0x77, 0x21, 0x8b, 0x2f, 0xa2, 0x89, 0x34, 0x92, 0x37, 0xe6, 0x0b, 0xb9, 0x67, + 0x7e, 0x34, 0x83, 0x70, 0xdc, 0xc3, 0x10, 0x3f, 0x81, 0xce, 0x53, 0xbd, 0xa8, 0xa7, 0x37, 0xf0, + 0x49, 0x74, 0x21, 0x91, 0x42, 0x68, 0x5e, 0x11, 0x3d, 0x9e, 0x42, 0xc2, 0x1b, 0x77, 0x1e, 0x29, + 0xc9, 0x04, 0xb4, 0x69, 0x53, 0xe5, 0xf7, 0xfe, 0xd3, 0xc5, 0x9e, 0xf7, 0xbe, 0x79, 0x31, 0xf3, + 0xbb, 0xdf, 0xbc, 0x98, 0xf9, 0xa3, 0x6f, 0x5e, 0xcc, 0x7c, 0xea, 0xfa, 0x5e, 0x1c, 0x30, 0xd9, + 0x68, 0x5f, 0xe9, 0x03, 0x57, 0xa3, 0x1b, 0xff, 0x2f, 0x00, 0x00, 0xff, 0xff, 0xe3, 0xba, 0x6d, + 0x64, 0x80, 0x37, 0x01, 0x00, } func (m *Metadata) Marshal() (dAtA []byte, err error) { @@ -30573,6 +30586,16 @@ func (m *AutoUpdateConfigCreate) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + { + size, err := m.Status.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a { size, err := m.ConnectionMetadata.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -30640,6 +30663,16 @@ func (m *AutoUpdateConfigUpdate) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + { + size, err := m.ResourceMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a { size, err := m.ConnectionMetadata.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -30707,6 +30740,16 @@ func (m *AutoUpdateConfigDelete) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + { + size, err := m.Status.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a { size, err := m.ConnectionMetadata.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -30774,6 +30817,16 @@ func (m *AutoUpdateVersionCreate) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + { + size, err := m.Status.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a { size, err := m.ConnectionMetadata.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -30841,6 +30894,16 @@ func (m *AutoUpdateVersionUpdate) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + { + size, err := m.ResourceMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a { size, err := m.ConnectionMetadata.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -30908,6 +30971,16 @@ func (m *AutoUpdateVersionDelete) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + { + size, err := m.Status.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a { size, err := m.ConnectionMetadata.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -37864,6 +37937,8 @@ func (m *AutoUpdateConfigCreate) Size() (n int) { n += 1 + l + sovEvents(uint64(l)) l = m.ConnectionMetadata.Size() n += 1 + l + sovEvents(uint64(l)) + l = m.Status.Size() + n += 1 + l + sovEvents(uint64(l)) if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -37884,6 +37959,8 @@ func (m *AutoUpdateConfigUpdate) Size() (n int) { n += 1 + l + sovEvents(uint64(l)) l = m.ConnectionMetadata.Size() n += 1 + l + sovEvents(uint64(l)) + l = m.ResourceMetadata.Size() + n += 1 + l + sovEvents(uint64(l)) if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -37904,6 +37981,8 @@ func (m *AutoUpdateConfigDelete) Size() (n int) { n += 1 + l + sovEvents(uint64(l)) l = m.ConnectionMetadata.Size() n += 1 + l + sovEvents(uint64(l)) + l = m.Status.Size() + n += 1 + l + sovEvents(uint64(l)) if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -37924,6 +38003,8 @@ func (m *AutoUpdateVersionCreate) Size() (n int) { n += 1 + l + sovEvents(uint64(l)) l = m.ConnectionMetadata.Size() n += 1 + l + sovEvents(uint64(l)) + l = m.Status.Size() + n += 1 + l + sovEvents(uint64(l)) if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -37944,6 +38025,8 @@ func (m *AutoUpdateVersionUpdate) Size() (n int) { n += 1 + l + sovEvents(uint64(l)) l = m.ConnectionMetadata.Size() n += 1 + l + sovEvents(uint64(l)) + l = m.ResourceMetadata.Size() + n += 1 + l + sovEvents(uint64(l)) if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -37964,6 +38047,8 @@ func (m *AutoUpdateVersionDelete) Size() (n int) { n += 1 + l + sovEvents(uint64(l)) l = m.ConnectionMetadata.Size() n += 1 + l + sovEvents(uint64(l)) + l = m.Status.Size() + n += 1 + l + sovEvents(uint64(l)) if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -84993,6 +85078,39 @@ func (m *AutoUpdateConfigCreate) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Status.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) @@ -85176,6 +85294,39 @@ func (m *AutoUpdateConfigUpdate) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ResourceMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) @@ -85359,6 +85510,39 @@ func (m *AutoUpdateConfigDelete) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Status.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) @@ -85542,6 +85726,39 @@ func (m *AutoUpdateVersionCreate) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Status.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) @@ -85725,6 +85942,39 @@ func (m *AutoUpdateVersionUpdate) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ResourceMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) @@ -85908,6 +86158,39 @@ func (m *AutoUpdateVersionDelete) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Status.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) diff --git a/api/types/events/oneof.go b/api/types/events/oneof.go index a66527a2918a8..2f8c0f5cf47e6 100644 --- a/api/types/events/oneof.go +++ b/api/types/events/oneof.go @@ -611,7 +611,31 @@ func ToOneOf(in AuditEvent) (*OneOf, error) { out.Event = &OneOf_IntegrationDelete{ IntegrationDelete: e, } + case *AutoUpdateConfigCreate: + out.Event = &OneOf_AutoUpdateConfigCreate{ + AutoUpdateConfigCreate: e, + } + case *AutoUpdateConfigUpdate: + out.Event = &OneOf_AutoUpdateConfigUpdate{ + AutoUpdateConfigUpdate: e, + } + case *AutoUpdateConfigDelete: + out.Event = &OneOf_AutoUpdateConfigDelete{ + AutoUpdateConfigDelete: e, + } + case *AutoUpdateVersionCreate: + out.Event = &OneOf_AutoUpdateVersionCreate{ + AutoUpdateVersionCreate: e, + } + case *AutoUpdateVersionUpdate: + out.Event = &OneOf_AutoUpdateVersionUpdate{ + AutoUpdateVersionUpdate: e, + } + case *AutoUpdateVersionDelete: + out.Event = &OneOf_AutoUpdateVersionDelete{ + AutoUpdateVersionDelete: e, + } default: log.Errorf("Attempted to convert dynamic event of unknown type \"%v\" into protobuf event.", in.GetType()) unknown := &Unknown{} diff --git a/go.mod b/go.mod index 5d8873bc7e3bb..5de838c5a3f01 100644 --- a/go.mod +++ b/go.mod @@ -213,6 +213,7 @@ require ( github.com/Masterminds/sprig/v3 v3.2.3 github.com/distribution/reference v0.5.0 github.com/google/go-containerregistry v0.19.1 + github.com/google/renameio/v2 v2.0.0 github.com/opencontainers/go-digest v1.0.0 github.com/sigstore/cosign/v2 v2.2.4 github.com/sigstore/sigstore v1.8.3 @@ -319,7 +320,6 @@ require ( github.com/google/go-configfs-tsm v0.2.2 // indirect github.com/google/go-tspi v0.3.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/renameio/v2 v2.0.0 // indirect github.com/google/s2a-go v0.1.7 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/gorilla/handlers v1.5.1 // indirect diff --git a/integration/autoupdate/tools/helper_test.go b/integration/autoupdate/tools/helper_test.go new file mode 100644 index 0000000000000..a3c37a9e94b55 --- /dev/null +++ b/integration/autoupdate/tools/helper_test.go @@ -0,0 +1,89 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package tools_test + +import ( + "net/http" + "sync" +) + +type limitRequest struct { + limit int64 + lock chan struct{} +} + +// limitedResponseWriter wraps http.ResponseWriter and enforces a write limit +// then block the response until signal is received. +type limitedResponseWriter struct { + requests chan limitRequest +} + +// newLimitedResponseWriter creates a new limitedResponseWriter with the lock. +func newLimitedResponseWriter() *limitedResponseWriter { + lw := &limitedResponseWriter{ + requests: make(chan limitRequest, 10), + } + return lw +} + +// Wrap wraps response writer if limit was previously requested, if not, return original one. +func (lw *limitedResponseWriter) Wrap(w http.ResponseWriter) http.ResponseWriter { + select { + case request := <-lw.requests: + return &wrapper{ + ResponseWriter: w, + request: request, + } + default: + return w + } +} + +// SetLimitRequest sends limit request to the pool to wrap next response writer with defined limits. +func (lw *limitedResponseWriter) SetLimitRequest(limit limitRequest) { + lw.requests <- limit +} + +// wrapper wraps the http response writer to control writing operation by blocking it. +type wrapper struct { + http.ResponseWriter + + written int64 + request limitRequest + released bool + + mutex sync.Mutex +} + +// Write writes data to the underlying ResponseWriter but respects the byte limit. +func (lw *wrapper) Write(p []byte) (int, error) { + lw.mutex.Lock() + defer lw.mutex.Unlock() + + if lw.written >= lw.request.limit && !lw.released { + // Send signal that lock is acquired and wait till it was released by response. + lw.request.lock <- struct{}{} + <-lw.request.lock + lw.released = true + } + + n, err := lw.ResponseWriter.Write(p) + lw.written += int64(n) + return n, err +} diff --git a/integration/autoupdate/tools/helper_unix_test.go b/integration/autoupdate/tools/helper_unix_test.go new file mode 100644 index 0000000000000..61ba0766b90d4 --- /dev/null +++ b/integration/autoupdate/tools/helper_unix_test.go @@ -0,0 +1,37 @@ +//go:build !windows + +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package tools_test + +import ( + "errors" + "syscall" + + "github.com/gravitational/trace" +) + +// sendInterrupt sends a SIGINT to the process. +func sendInterrupt(pid int) error { + err := syscall.Kill(pid, syscall.SIGINT) + if errors.Is(err, syscall.ESRCH) { + return trace.BadParameter("can't find the process: %v", pid) + } + return trace.Wrap(err) +} diff --git a/integration/autoupdate/tools/helper_windows_test.go b/integration/autoupdate/tools/helper_windows_test.go new file mode 100644 index 0000000000000..b2ede9ade8c19 --- /dev/null +++ b/integration/autoupdate/tools/helper_windows_test.go @@ -0,0 +1,42 @@ +//go:build windows + +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package tools_test + +import ( + "syscall" + + "github.com/gravitational/trace" + "golang.org/x/sys/windows" +) + +var ( + kernel = windows.NewLazyDLL("kernel32.dll") + ctrlEvent = kernel.NewProc("GenerateConsoleCtrlEvent") +) + +// sendInterrupt sends a Ctrl-Break event to the process. +func sendInterrupt(pid int) error { + r, _, err := ctrlEvent.Call(uintptr(syscall.CTRL_BREAK_EVENT), uintptr(pid)) + if r == 0 { + return trace.Wrap(err) + } + return nil +} diff --git a/integration/autoupdate/tools/main_test.go b/integration/autoupdate/tools/main_test.go new file mode 100644 index 0000000000000..db203f84c0fb5 --- /dev/null +++ b/integration/autoupdate/tools/main_test.go @@ -0,0 +1,174 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package tools_test + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "errors" + "fmt" + "io" + "log" + "net/http" + "net/http/httptest" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" + + "github.com/gravitational/trace" + + "github.com/gravitational/teleport/api/constants" + "github.com/gravitational/teleport/integration/helpers/archive" +) + +const ( + testBinaryName = "updater" + teleportToolsVersion = "TELEPORT_TOOLS_VERSION" +) + +var ( + // testVersions list of the pre-compiled binaries with encoded versions to check. + testVersions = []string{ + "1.2.3", + "3.2.1", + } + limitedWriter = newLimitedResponseWriter() + + toolsDir string + baseURL string +) + +func TestMain(m *testing.M) { + ctx := context.Background() + tmp, err := os.MkdirTemp(os.TempDir(), testBinaryName) + if err != nil { + log.Fatalf("failed to create temporary directory: %v", err) + } + + toolsDir, err = os.MkdirTemp(os.TempDir(), "tools") + if err != nil { + log.Fatalf("failed to create temporary directory: %v", err) + } + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + filePath := filepath.Join(tmp, r.URL.Path) + switch { + case strings.HasSuffix(r.URL.Path, ".sha256"): + serve256File(w, r, strings.TrimSuffix(filePath, ".sha256")) + default: + http.ServeFile(limitedWriter.Wrap(w), r, filePath) + } + })) + baseURL = server.URL + for _, version := range testVersions { + if err := buildAndArchiveApps(ctx, tmp, version, server.URL); err != nil { + log.Fatalf("failed to build testing app binary archive: %v", err) + } + } + + // Run tests after binary is built. + code := m.Run() + + server.Close() + if err := os.RemoveAll(tmp); err != nil { + log.Fatalf("failed to remove temporary directory: %v", err) + } + if err := os.RemoveAll(toolsDir); err != nil { + log.Fatalf("failed to remove tools directory: %v", err) + } + + os.Exit(code) +} + +// serve256File calculates sha256 checksum for requested file. +func serve256File(w http.ResponseWriter, _ *http.Request, filePath string) { + log.Printf("Calculating and serving file checksum: %s\n", filePath) + + w.Header().Set("Content-Disposition", "attachment; filename=\""+filepath.Base(filePath)+".sha256\"") + w.Header().Set("Content-Type", "plain/text") + + file, err := os.Open(filePath) + if errors.Is(err, os.ErrNotExist) { + http.Error(w, "file not found", http.StatusNotFound) + return + } + if err != nil { + http.Error(w, "failed to open file", http.StatusInternalServerError) + return + } + defer file.Close() + + hash := sha256.New() + if _, err := io.Copy(hash, file); err != nil { + http.Error(w, "failed to write to hash", http.StatusInternalServerError) + return + } + if _, err := hex.NewEncoder(w).Write(hash.Sum(nil)); err != nil { + http.Error(w, "failed to write checksum", http.StatusInternalServerError) + } +} + +// buildAndArchiveApps compiles the updater integration and pack it depends on platform is used. +func buildAndArchiveApps(ctx context.Context, path string, version string, baseURL string) error { + versionPath := filepath.Join(path, version) + for _, app := range []string{"tsh", "tctl"} { + output := filepath.Join(versionPath, app) + switch runtime.GOOS { + case constants.WindowsOS: + output = filepath.Join(versionPath, app+".exe") + case constants.DarwinOS: + output = filepath.Join(versionPath, app+".app", "Contents", "MacOS", app) + } + if err := buildBinary(output, version, baseURL, app); err != nil { + return trace.Wrap(err) + } + } + switch runtime.GOOS { + case constants.DarwinOS: + archivePath := filepath.Join(path, fmt.Sprintf("teleport-%s.pkg", version)) + return trace.Wrap(archive.CompressDirToPkgFile(ctx, versionPath, archivePath, "com.example.pkgtest")) + case constants.WindowsOS: + archivePath := filepath.Join(path, fmt.Sprintf("teleport-v%s-windows-amd64-bin.zip", version)) + return trace.Wrap(archive.CompressDirToZipFile(ctx, versionPath, archivePath)) + default: + archivePath := filepath.Join(path, fmt.Sprintf("teleport-v%s-linux-%s-bin.tar.gz", version, runtime.GOARCH)) + return trace.Wrap(archive.CompressDirToTarGzFile(ctx, versionPath, archivePath)) + } +} + +// buildBinary executes command to build client tool binary with updater logic for testing. +func buildBinary(output string, version string, baseURL string, app string) error { + cmd := exec.Command( + "go", "build", "-o", output, + "-ldflags", strings.Join([]string{ + fmt.Sprintf("-X 'github.com/gravitational/teleport/integration/autoupdate/tools/updater.version=%s'", version), + fmt.Sprintf("-X 'github.com/gravitational/teleport/lib/autoupdate/tools.version=%s'", version), + fmt.Sprintf("-X 'github.com/gravitational/teleport/lib/autoupdate/tools.baseURL=%s'", baseURL), + }, " "), + fmt.Sprintf("./updater/%s", app), + ) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + return trace.Wrap(cmd.Run()) +} diff --git a/integration/autoupdate/tools/updater/modules.go b/integration/autoupdate/tools/updater/modules.go new file mode 100644 index 0000000000000..2f5d806ce1877 --- /dev/null +++ b/integration/autoupdate/tools/updater/modules.go @@ -0,0 +1,104 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package updater + +import ( + "context" + "crypto" + "fmt" + "time" + + "github.com/gravitational/trace" + + "github.com/gravitational/teleport" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/types/accesslist" + "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/lib/modules" + "github.com/gravitational/teleport/lib/tlsca" +) + +// TestPassword is password generated during the test to login in test cluster. +const TestPassword = "UPDATER_TEST_PASSWORD" + +var ( + version = teleport.Version +) + +type TestModules struct{} + +func (p *TestModules) GenerateAccessRequestPromotions(context.Context, modules.AccessResourcesGetter, types.AccessRequest) (*types.AccessRequestAllowedPromotions, error) { + return &types.AccessRequestAllowedPromotions{}, nil +} + +func (p *TestModules) GetSuggestedAccessLists(ctx context.Context, identity *tlsca.Identity, clt modules.AccessListSuggestionClient, accessListGetter modules.AccessListGetter, requestID string) ([]*accesslist.AccessList, error) { + return []*accesslist.AccessList{}, nil +} + +// BuildType returns build type (OSS or Enterprise) +func (p *TestModules) BuildType() string { + return "CLI" +} + +// IsEnterpriseBuild returns true if `UPDATER_TEST_BUILD` env is set `ent` for [TestModules]. +func (p *TestModules) IsEnterpriseBuild() bool { + return false +} + +// IsOSSBuild returns true if `UPDATER_TEST_BUILD` env is set `oss` for [TestModules]. +func (p *TestModules) IsOSSBuild() bool { + return false +} + +// LicenseExpiry returns the expiry date of the enterprise license, if applicable. +func (p *TestModules) LicenseExpiry() time.Time { + return time.Time{} +} + +// PrintVersion prints the Teleport version. +func (p *TestModules) PrintVersion() { + fmt.Printf("Teleport v%v git\n", version) +} + +// Features returns supported features +func (p *TestModules) Features() modules.Features { + return modules.Features{ + AdvancedAccessWorkflows: true, + } +} + +// IsBoringBinary checks if the binary was compiled with BoringCrypto. +func (p *TestModules) IsBoringBinary() bool { + return false +} + +// AttestHardwareKey attests a hardware key. +func (p *TestModules) AttestHardwareKey(context.Context, interface{}, *keys.AttestationStatement, crypto.PublicKey, time.Duration) (*keys.AttestationData, error) { + return nil, trace.NotFound("no attestation data for the given key") +} + +func (p *TestModules) EnableRecoveryCodes() {} + +func (p *TestModules) EnablePlugins() {} + +func (p *TestModules) SetFeatures(f modules.Features) {} + +func (p *TestModules) EnableAccessGraph() {} + +func (p *TestModules) EnableAccessMonitoring() {} diff --git a/integration/autoupdate/tools/updater/tctl/main.go b/integration/autoupdate/tools/updater/tctl/main.go new file mode 100644 index 0000000000000..099ea24ba226b --- /dev/null +++ b/integration/autoupdate/tools/updater/tctl/main.go @@ -0,0 +1,37 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package main + +import ( + "context" + + "github.com/gravitational/teleport/integration/autoupdate/tools/updater" + "github.com/gravitational/teleport/lib/modules" + stacksignal "github.com/gravitational/teleport/lib/utils/signal" + tctl "github.com/gravitational/teleport/tool/tctl/common" +) + +func main() { + ctx, cancel := stacksignal.GetSignalHandler().NotifyContext(context.Background()) + defer cancel() + + modules.SetModules(&updater.TestModules{}) + + tctl.Run(ctx, tctl.Commands()) +} diff --git a/integration/autoupdate/tools/updater/tsh/main.go b/integration/autoupdate/tools/updater/tsh/main.go new file mode 100644 index 0000000000000..eab022e08d336 --- /dev/null +++ b/integration/autoupdate/tools/updater/tsh/main.go @@ -0,0 +1,44 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package main + +import ( + "context" + "os" + + "github.com/gravitational/teleport/api/utils/prompt" + "github.com/gravitational/teleport/integration/autoupdate/tools/updater" + "github.com/gravitational/teleport/lib/modules" + "github.com/gravitational/teleport/lib/utils" + stacksignal "github.com/gravitational/teleport/lib/utils/signal" + tsh "github.com/gravitational/teleport/tool/tsh/common" +) + +func main() { + ctx, cancel := stacksignal.GetSignalHandler().NotifyContext(context.Background()) + defer cancel() + + modules.SetModules(&updater.TestModules{}) + prompt.SetStdin(prompt.NewFakeReader().AddString(os.Getenv(updater.TestPassword))) + + err := tsh.Run(ctx, os.Args[1:]) + if err != nil { + utils.FatalError(err) + } +} diff --git a/integration/autoupdate/tools/updater_test.go b/integration/autoupdate/tools/updater_test.go new file mode 100644 index 0000000000000..0c05f2524caf0 --- /dev/null +++ b/integration/autoupdate/tools/updater_test.go @@ -0,0 +1,218 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package tools_test + +import ( + "bytes" + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + "regexp" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/autoupdate/tools" +) + +var ( + // pattern is template for response on version command for client tools {tsh, tctl}. + pattern = regexp.MustCompile(`(?m)Teleport v(.*) git`) +) + +// TestUpdate verifies the basic update logic. We first download a lower version, then request +// an update to a newer version, expecting it to re-execute with the updated version. +func TestUpdate(t *testing.T) { + t.Setenv(types.HomeEnvVar, t.TempDir()) + ctx := context.Background() + + // Fetch compiled test binary with updater logic and install to $TELEPORT_HOME. + updater := tools.NewUpdater( + toolsDir, + testVersions[0], + tools.WithBaseURL(baseURL), + ) + err := updater.Update(ctx, testVersions[0]) + require.NoError(t, err) + + // Verify that the installed version is equal to requested one. + cmd := exec.CommandContext(ctx, filepath.Join(toolsDir, "tctl"), "version") + out, err := cmd.Output() + require.NoError(t, err) + + matches := pattern.FindStringSubmatch(string(out)) + require.Len(t, matches, 2) + require.Equal(t, testVersions[0], matches[1]) + + // Execute version command again with setting the new version which must + // trigger re-execution of the same command after downloading requested version. + cmd = exec.CommandContext(ctx, filepath.Join(toolsDir, "tsh"), "version") + cmd.Env = append( + os.Environ(), + fmt.Sprintf("%s=%s", teleportToolsVersion, testVersions[1]), + ) + out, err = cmd.Output() + require.NoError(t, err) + + matches = pattern.FindStringSubmatch(string(out)) + require.Len(t, matches, 2) + require.Equal(t, testVersions[1], matches[1]) +} + +// TestParallelUpdate launches multiple updater commands in parallel while defining a new version. +// The first process should acquire a lock and block execution for the other processes. After the +// first update is complete, other processes should acquire the lock one by one and re-execute +// the command with the updated version without any new downloads. +func TestParallelUpdate(t *testing.T) { + t.Setenv(types.HomeEnvVar, t.TempDir()) + ctx := context.Background() + + // Initial fetch the updater binary un-archive and replace. + updater := tools.NewUpdater( + toolsDir, + testVersions[0], + tools.WithBaseURL(baseURL), + ) + err := updater.Update(ctx, testVersions[0]) + require.NoError(t, err) + + // By setting the limit request next test http serving file going blocked until unlock is sent. + lock := make(chan struct{}) + limitedWriter.SetLimitRequest(limitRequest{ + limit: 1024, + lock: lock, + }) + + outputs := make([]bytes.Buffer, 3) + errChan := make(chan error, 3) + for i := 0; i < len(outputs); i++ { + cmd := exec.Command(filepath.Join(toolsDir, "tsh"), "version") + cmd.Stdout = &outputs[i] + cmd.Stderr = &outputs[i] + cmd.Env = append( + os.Environ(), + fmt.Sprintf("%s=%s", teleportToolsVersion, testVersions[1]), + ) + err = cmd.Start() + require.NoError(t, err, "failed to start updater") + + go func(cmd *exec.Cmd) { + errChan <- cmd.Wait() + }(cmd) + } + + select { + case err := <-errChan: + require.Fail(t, "we shouldn't receive any error", err) + case <-time.After(5 * time.Second): + require.Fail(t, "failed to wait till the download is started") + case <-lock: + // Wait for a short period to allow other processes to launch and attempt to acquire the lock. + time.Sleep(100 * time.Millisecond) + lock <- struct{}{} + } + + // Wait till process finished with exit code 0, but we still should get progress + // bar in output content. + for i := 0; i < cap(outputs); i++ { + select { + case <-time.After(5 * time.Second): + require.Fail(t, "failed to wait till the process is finished") + case err := <-errChan: + require.NoError(t, err) + } + } + + var progressCount int + for i := 0; i < cap(outputs); i++ { + matches := pattern.FindStringSubmatch(outputs[i].String()) + require.Len(t, matches, 2) + assert.Equal(t, testVersions[1], matches[1]) + if strings.Contains(outputs[i].String(), "Update progress:") { + progressCount++ + } + } + assert.Equal(t, 1, progressCount, "we should have only one progress bar downloading new version") +} + +// TestUpdateInterruptSignal verifies the interrupt signal send to the process must stop downloading. +func TestUpdateInterruptSignal(t *testing.T) { + t.Setenv(types.HomeEnvVar, t.TempDir()) + ctx := context.Background() + + // Initial fetch the updater binary un-archive and replace. + updater := tools.NewUpdater( + toolsDir, + testVersions[0], + tools.WithBaseURL(baseURL), + ) + err := updater.Update(ctx, testVersions[0]) + require.NoError(t, err) + + var output bytes.Buffer + cmd := exec.Command(filepath.Join(toolsDir, "tsh"), "version") + cmd.Stdout = &output + cmd.Stderr = &output + cmd.Env = append( + os.Environ(), + fmt.Sprintf("%s=%s", teleportToolsVersion, testVersions[1]), + ) + err = cmd.Start() + require.NoError(t, err, "failed to start updater") + pid := cmd.Process.Pid + + errChan := make(chan error) + go func() { + errChan <- cmd.Wait() + }() + + // By setting the limit request next test http serving file going blocked until unlock is sent. + lock := make(chan struct{}) + limitedWriter.SetLimitRequest(limitRequest{ + limit: 1024, + lock: lock, + }) + + select { + case err := <-errChan: + require.Fail(t, "we shouldn't receive any error", err) + case <-time.After(5 * time.Second): + require.Fail(t, "failed to wait till the download is started") + case <-lock: + time.Sleep(100 * time.Millisecond) + require.NoError(t, sendInterrupt(pid)) + lock <- struct{}{} + } + + // Wait till process finished with exit code 0, but we still should get progress + // bar in output content. + select { + case <-time.After(5 * time.Second): + require.Fail(t, "failed to wait till the process interrupted") + case err := <-errChan: + require.NoError(t, err) + } + assert.Contains(t, output.String(), "Update progress:") +} diff --git a/integration/autoupdate/tools/updater_tsh_test.go b/integration/autoupdate/tools/updater_tsh_test.go new file mode 100644 index 0000000000000..0f631894c63fd --- /dev/null +++ b/integration/autoupdate/tools/updater_tsh_test.go @@ -0,0 +1,148 @@ +//go:build !windows + +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package tools_test + +import ( + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" + + "github.com/gravitational/teleport/api/constants" + autoupdatev1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/types/autoupdate" + "github.com/gravitational/teleport/integration/autoupdate/tools/updater" + "github.com/gravitational/teleport/lib/autoupdate/tools" + "github.com/gravitational/teleport/lib/utils" + testserver "github.com/gravitational/teleport/tool/teleport/testenv" + "github.com/gravitational/teleport/tool/tsh/common" +) + +// TestAliasLoginWithUpdater runs test cluster with enabled auto updates for client tools, +// checks that defined alias in tsh configuration is replaced to the proper login command +// and after auto update this not leads to recursive alias re-execution. +func TestAliasLoginWithUpdater(t *testing.T) { + ctx := context.Background() + + homeDir := filepath.Join(t.TempDir(), "home") + require.NoError(t, os.MkdirAll(homeDir, 0700)) + installDir := filepath.Join(t.TempDir(), "local") + require.NoError(t, os.MkdirAll(installDir, 0700)) + + t.Setenv(types.HomeEnvVar, homeDir) + + alice, err := types.NewUser("alice") + require.NoError(t, err) + alice.SetRoles([]string{"access"}) + + // Enable client tools auto updates and set the target version. + config, err := autoupdate.NewAutoUpdateConfig(&autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeEnabled, + }, + }) + require.NoError(t, err) + version, err := autoupdate.NewAutoUpdateVersion(&autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: testVersions[1], // [v3.2.1] + }, + }) + require.NoError(t, err) + + // Disable 2fa to simplify login for test. + ap, err := types.NewAuthPreferenceFromConfigFile(types.AuthPreferenceSpecV2{ + Type: constants.Local, + SecondFactor: constants.SecondFactorOff, + Webauthn: &types.Webauthn{ + RPID: "localhost", + }, + }) + require.NoError(t, err) + + rootServer := testserver.MakeTestServer(t, + testserver.WithBootstrap(alice), + testserver.WithClusterName(t, "root"), + testserver.WithAuthPreference(ap), + ) + authService := rootServer.GetAuthServer() + _, err = authService.UpsertAutoUpdateConfig(ctx, config) + require.NoError(t, err) + _, err = authService.UpsertAutoUpdateVersion(ctx, version) + require.NoError(t, err) + password, err := utils.CryptoRandomHex(6) + require.NoError(t, err) + t.Setenv(updater.TestPassword, password) + err = authService.UpsertPassword("alice", []byte(password)) + require.NoError(t, err) + + // Assign alias to the login command for test cluster. + proxyAddr, err := rootServer.ProxyWebAddr() + require.NoError(t, err) + configPath := filepath.Join(homeDir, "config/config.yaml") + require.NoError(t, os.MkdirAll(filepath.Dir(configPath), 0700)) + executable := filepath.Join(installDir, "tsh") + out, err := yaml.Marshal(common.TSHConfig{ + Aliases: map[string]string{ + "loginalice": fmt.Sprintf( + "%s login --insecure --proxy %s --user alice --auth %s", + executable, proxyAddr, constants.LocalConnector, + ), + }, + }) + require.NoError(t, err) + require.NoError(t, os.WriteFile(configPath, out, 0600)) + + // Fetch compiled test binary and install to tools dir [v1.2.3]. + err = tools.NewUpdater(installDir, testVersions[0], tools.WithBaseURL(baseURL)).Update(ctx, testVersions[0]) + require.NoError(t, err) + + // Execute alias command which must be transformed to the login command. + // Since client tools autoupdates is enabled and target version is set + // in the test cluster, we have to update client tools to new version. + cmd := exec.CommandContext(ctx, executable, "loginalice") + cmd.Env = os.Environ() + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + require.NoError(t, cmd.Run()) + + // Verify tctl status after login. + cmd = exec.CommandContext(ctx, filepath.Join(installDir, "tctl"), "status", "--insecure") + cmd.Env = os.Environ() + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + require.NoError(t, cmd.Run()) + + // Run version command to verify that login command executed auto update and + // tsh was upgraded to [v3.2.1]. + cmd = exec.CommandContext(ctx, executable, "version") + out, err = cmd.Output() + require.NoError(t, err) + + matches := pattern.FindStringSubmatch(string(out)) + require.Len(t, matches, 2) + require.Equal(t, testVersions[1], matches[1]) +} diff --git a/integration/helpers/archive/packaging.go b/integration/helpers/archive/packaging.go new file mode 100644 index 0000000000000..ee237749115a3 --- /dev/null +++ b/integration/helpers/archive/packaging.go @@ -0,0 +1,170 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package archive + +import ( + "archive/tar" + "archive/zip" + "compress/gzip" + "context" + "io" + "log/slog" + "os" + "os/exec" + "path/filepath" + "runtime" + + "github.com/gravitational/trace" + + "github.com/gravitational/teleport" +) + +// CompressDirToZipFile compresses a source directory into `.zip` format and stores at `archivePath`, +// preserving the relative file path structure of the source directory. +func CompressDirToZipFile(ctx context.Context, sourceDir, archivePath string) (err error) { + archive, err := os.Create(archivePath) + if err != nil { + return trace.Wrap(err) + } + defer func() { + if closeErr := archive.Close(); closeErr != nil { + err = trace.NewAggregate(err, closeErr) + return + } + if err != nil { + if err := os.Remove(archivePath); err != nil { + slog.ErrorContext(ctx, "failed to remove archive", "error", err) + } + } + }() + + zipWriter := zip.NewWriter(archive) + err = filepath.Walk(sourceDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return trace.Wrap(err) + } + if info.IsDir() { + return nil + } + file, err := os.Open(path) + if err != nil { + return trace.Wrap(err) + } + defer file.Close() + relPath, err := filepath.Rel(sourceDir, path) + if err != nil { + return trace.Wrap(err) + } + zipFileWriter, err := zipWriter.Create(relPath) + if err != nil { + return trace.Wrap(err) + } + if _, err = io.Copy(zipFileWriter, file); err != nil { + return trace.Wrap(err) + } + return trace.Wrap(file.Close()) + }) + if err != nil { + return trace.Wrap(err) + } + if err = zipWriter.Close(); err != nil { + return trace.Wrap(err) + } + + return +} + +// CompressDirToTarGzFile compresses a source directory into .tar.gz format and stores at `archivePath`, +// preserving the relative file path structure of the source directory. +func CompressDirToTarGzFile(ctx context.Context, sourceDir, archivePath string) (err error) { + archive, err := os.Create(archivePath) + if err != nil { + return trace.Wrap(err) + } + defer func() { + if closeErr := archive.Close(); closeErr != nil { + err = trace.NewAggregate(err, closeErr) + return + } + if err != nil { + if err := os.Remove(archivePath); err != nil { + slog.ErrorContext(ctx, "failed to remove archive", "error", err) + } + } + }() + gzipWriter := gzip.NewWriter(archive) + tarWriter := tar.NewWriter(gzipWriter) + err = filepath.Walk(sourceDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return nil + } + file, err := os.Open(path) + if err != nil { + return err + } + defer file.Close() + header, err := tar.FileInfoHeader(info, info.Name()) + if err != nil { + return trace.Wrap(err) + } + header.Name, err = filepath.Rel(sourceDir, path) + if err != nil { + return trace.Wrap(err) + } + if err := tarWriter.WriteHeader(header); err != nil { + return trace.Wrap(err) + } + if _, err = io.Copy(tarWriter, file); err != nil { + return trace.Wrap(err) + } + return trace.Wrap(file.Close()) + }) + if err != nil { + return trace.Wrap(err) + } + if err = tarWriter.Close(); err != nil { + return trace.Wrap(err) + } + if err = gzipWriter.Close(); err != nil { + return trace.Wrap(err) + } + + return +} + +// CompressDirToPkgFile runs for the macOS `pkgbuild` command to generate a .pkg +// archive file from the source directory. +func CompressDirToPkgFile(ctx context.Context, sourceDir, archivePath, identifier string) error { + if runtime.GOOS != "darwin" { + return trace.BadParameter("only darwin platform is supported for pkg file") + } + cmd := exec.CommandContext( + ctx, + "pkgbuild", + "--root", sourceDir, + "--identifier", identifier, + "--version", teleport.Version, + archivePath, + ) + + return trace.Wrap(cmd.Run()) +} diff --git a/lib/auth/autoupdate/autoupdatev1/service.go b/lib/auth/autoupdate/autoupdatev1/service.go index 1c6faf8c04b58..5bc902b739663 100644 --- a/lib/auth/autoupdate/autoupdatev1/service.go +++ b/lib/auth/autoupdate/autoupdatev1/service.go @@ -20,13 +20,17 @@ package autoupdatev1 import ( "context" + "log/slog" "github.com/gravitational/trace" "google.golang.org/protobuf/types/known/emptypb" "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1" "github.com/gravitational/teleport/api/types" + apievents "github.com/gravitational/teleport/api/types/events" "github.com/gravitational/teleport/lib/authz" + "github.com/gravitational/teleport/lib/events" + "github.com/gravitational/teleport/lib/modules" "github.com/gravitational/teleport/lib/services" ) @@ -47,6 +51,8 @@ type ServiceConfig struct { Backend services.AutoUpdateService // Cache is the cache used to store AutoUpdate resources. Cache Cache + // Emitter is the event emitter. + Emitter apievents.Emitter } // Service implements the gRPC API layer for the AutoUpdate. @@ -55,6 +61,7 @@ type Service struct { authorizer authz.Authorizer backend services.AutoUpdateService + emitter apievents.Emitter cache Cache } @@ -67,11 +74,14 @@ func NewService(cfg ServiceConfig) (*Service, error) { return nil, trace.BadParameter("authorizer is required") case cfg.Cache == nil: return nil, trace.BadParameter("cache is required") + case cfg.Emitter == nil: + return nil, trace.BadParameter("Emitter is required") } return &Service{ authorizer: cfg.Authorizer, backend: cfg.Backend, cache: cfg.Cache, + emitter: cfg.Emitter, }, nil } @@ -106,6 +116,27 @@ func (s *Service) CreateAutoUpdateConfig(ctx context.Context, req *autoupdate.Cr } config, err := s.backend.CreateAutoUpdateConfig(ctx, req.Config) + var errMsg string + if err != nil { + errMsg = err.Error() + } + userMetadata := authz.ClientUserMetadata(ctx) + s.emitEvent(ctx, &apievents.AutoUpdateConfigCreate{ + Metadata: apievents.Metadata{ + Type: events.AutoUpdateConfigCreateEvent, + Code: events.AutoUpdateConfigCreateCode, + }, + UserMetadata: userMetadata, + ResourceMetadata: apievents.ResourceMetadata{ + Name: types.MetaNameAutoUpdateConfig, + UpdatedBy: userMetadata.User, + }, + ConnectionMetadata: authz.ConnectionMetadata(ctx), + Status: apievents.Status{ + Success: err == nil, + Error: errMsg, + }, + }) return config, trace.Wrap(err) } @@ -121,6 +152,27 @@ func (s *Service) UpdateAutoUpdateConfig(ctx context.Context, req *autoupdate.Up } config, err := s.backend.UpdateAutoUpdateConfig(ctx, req.Config) + var errMsg string + if err != nil { + errMsg = err.Error() + } + userMetadata := authz.ClientUserMetadata(ctx) + s.emitEvent(ctx, &apievents.AutoUpdateConfigUpdate{ + Metadata: apievents.Metadata{ + Type: events.AutoUpdateConfigUpdateEvent, + Code: events.AutoUpdateConfigUpdateCode, + }, + UserMetadata: userMetadata, + ResourceMetadata: apievents.ResourceMetadata{ + Name: types.MetaNameAutoUpdateConfig, + UpdatedBy: userMetadata.User, + }, + ConnectionMetadata: authz.ConnectionMetadata(ctx), + Status: apievents.Status{ + Success: err == nil, + Error: errMsg, + }, + }) return config, trace.Wrap(err) } @@ -136,6 +188,27 @@ func (s *Service) UpsertAutoUpdateConfig(ctx context.Context, req *autoupdate.Up } config, err := s.backend.UpsertAutoUpdateConfig(ctx, req.Config) + var errMsg string + if err != nil { + errMsg = err.Error() + } + userMetadata := authz.ClientUserMetadata(ctx) + s.emitEvent(ctx, &apievents.AutoUpdateConfigUpdate{ + Metadata: apievents.Metadata{ + Type: events.AutoUpdateConfigUpdateEvent, + Code: events.AutoUpdateConfigUpdateCode, + }, + UserMetadata: userMetadata, + ResourceMetadata: apievents.ResourceMetadata{ + Name: types.MetaNameAutoUpdateConfig, + UpdatedBy: userMetadata.User, + }, + ConnectionMetadata: authz.ConnectionMetadata(ctx), + Status: apievents.Status{ + Success: err == nil, + Error: errMsg, + }, + }) return config, trace.Wrap(err) } @@ -150,10 +223,29 @@ func (s *Service) DeleteAutoUpdateConfig(ctx context.Context, req *autoupdate.De return nil, trace.Wrap(err) } - if err := s.backend.DeleteAutoUpdateConfig(ctx); err != nil { - return nil, trace.Wrap(err) + err = s.backend.DeleteAutoUpdateConfig(ctx) + var errMsg string + if err != nil { + errMsg = err.Error() } - return &emptypb.Empty{}, nil + userMetadata := authz.ClientUserMetadata(ctx) + s.emitEvent(ctx, &apievents.AutoUpdateConfigDelete{ + Metadata: apievents.Metadata{ + Type: events.AutoUpdateConfigDeleteEvent, + Code: events.AutoUpdateConfigDeleteCode, + }, + UserMetadata: userMetadata, + ResourceMetadata: apievents.ResourceMetadata{ + Name: types.MetaNameAutoUpdateConfig, + UpdatedBy: userMetadata.User, + }, + ConnectionMetadata: authz.ConnectionMetadata(ctx), + Status: apievents.Status{ + Success: err == nil, + Error: errMsg, + }, + }) + return &emptypb.Empty{}, trace.Wrap(err) } // GetAutoUpdateVersion gets the current AutoUpdateVersion singleton. @@ -182,11 +274,37 @@ func (s *Service) CreateAutoUpdateVersion(ctx context.Context, req *autoupdate.C return nil, trace.Wrap(err) } + if err := checkAdminCloudAccess(authCtx); err != nil { + return nil, trace.Wrap(err) + } + if err := authCtx.CheckAccessToKind(types.KindAutoUpdateVersion, types.VerbCreate); err != nil { return nil, trace.Wrap(err) } autoUpdateVersion, err := s.backend.CreateAutoUpdateVersion(ctx, req.Version) + var errMsg string + if err != nil { + errMsg = err.Error() + } + userMetadata := authz.ClientUserMetadata(ctx) + s.emitEvent(ctx, &apievents.AutoUpdateVersionCreate{ + Metadata: apievents.Metadata{ + Type: events.AutoUpdateVersionCreateEvent, + Code: events.AutoUpdateVersionCreateCode, + }, + UserMetadata: userMetadata, + ResourceMetadata: apievents.ResourceMetadata{ + Name: types.MetaNameAutoUpdateVersion, + UpdatedBy: userMetadata.User, + }, + ConnectionMetadata: authz.ConnectionMetadata(ctx), + Status: apievents.Status{ + Success: err == nil, + Error: errMsg, + }, + }) + return autoUpdateVersion, trace.Wrap(err) } @@ -197,11 +315,37 @@ func (s *Service) UpdateAutoUpdateVersion(ctx context.Context, req *autoupdate.U return nil, trace.Wrap(err) } + if err := checkAdminCloudAccess(authCtx); err != nil { + return nil, trace.Wrap(err) + } + if err := authCtx.CheckAccessToKind(types.KindAutoUpdateVersion, types.VerbUpdate); err != nil { return nil, trace.Wrap(err) } autoUpdateVersion, err := s.backend.UpdateAutoUpdateVersion(ctx, req.Version) + var errMsg string + if err != nil { + errMsg = err.Error() + } + userMetadata := authz.ClientUserMetadata(ctx) + s.emitEvent(ctx, &apievents.AutoUpdateVersionUpdate{ + Metadata: apievents.Metadata{ + Type: events.AutoUpdateVersionUpdateEvent, + Code: events.AutoUpdateVersionUpdateCode, + }, + UserMetadata: userMetadata, + ResourceMetadata: apievents.ResourceMetadata{ + Name: types.MetaNameAutoUpdateVersion, + UpdatedBy: userMetadata.User, + }, + ConnectionMetadata: authz.ConnectionMetadata(ctx), + Status: apievents.Status{ + Success: err == nil, + Error: errMsg, + }, + }) + return autoUpdateVersion, trace.Wrap(err) } @@ -212,11 +356,37 @@ func (s *Service) UpsertAutoUpdateVersion(ctx context.Context, req *autoupdate.U return nil, trace.Wrap(err) } + if err := checkAdminCloudAccess(authCtx); err != nil { + return nil, trace.Wrap(err) + } + if err := authCtx.CheckAccessToKind(types.KindAutoUpdateVersion, types.VerbCreate, types.VerbUpdate); err != nil { return nil, trace.Wrap(err) } autoUpdateVersion, err := s.backend.UpsertAutoUpdateVersion(ctx, req.Version) + var errMsg string + if err != nil { + errMsg = err.Error() + } + userMetadata := authz.ClientUserMetadata(ctx) + s.emitEvent(ctx, &apievents.AutoUpdateVersionUpdate{ + Metadata: apievents.Metadata{ + Type: events.AutoUpdateVersionUpdateEvent, + Code: events.AutoUpdateVersionUpdateCode, + }, + UserMetadata: userMetadata, + ResourceMetadata: apievents.ResourceMetadata{ + Name: types.MetaNameAutoUpdateVersion, + UpdatedBy: userMetadata.User, + }, + ConnectionMetadata: authz.ConnectionMetadata(ctx), + Status: apievents.Status{ + Success: err == nil, + Error: errMsg, + }, + }) + return autoUpdateVersion, trace.Wrap(err) } @@ -227,12 +397,55 @@ func (s *Service) DeleteAutoUpdateVersion(ctx context.Context, req *autoupdate.D return nil, trace.Wrap(err) } - if err := authCtx.CheckAccessToKind(types.KindAutoUpdateVersion, types.VerbDelete); err != nil { + if err := checkAdminCloudAccess(authCtx); err != nil { return nil, trace.Wrap(err) } - if err := s.backend.DeleteAutoUpdateVersion(ctx); err != nil { + if err := authCtx.CheckAccessToKind(types.KindAutoUpdateVersion, types.VerbDelete); err != nil { return nil, trace.Wrap(err) } - return &emptypb.Empty{}, nil + + err = s.backend.DeleteAutoUpdateVersion(ctx) + var errMsg string + if err != nil { + errMsg = err.Error() + } + userMetadata := authz.ClientUserMetadata(ctx) + s.emitEvent(ctx, &apievents.AutoUpdateVersionDelete{ + Metadata: apievents.Metadata{ + Type: events.AutoUpdateVersionDeleteEvent, + Code: events.AutoUpdateVersionDeleteCode, + }, + UserMetadata: userMetadata, + ResourceMetadata: apievents.ResourceMetadata{ + Name: types.MetaNameAutoUpdateVersion, + UpdatedBy: userMetadata.User, + }, + ConnectionMetadata: authz.ConnectionMetadata(ctx), + Status: apievents.Status{ + Success: err == nil, + Error: errMsg, + }, + }) + return &emptypb.Empty{}, trace.Wrap(err) +} + +func (s *Service) emitEvent(ctx context.Context, e apievents.AuditEvent) { + if err := s.emitter.EmitAuditEvent(ctx, e); err != nil { + slog.WarnContext(ctx, "Failed to emit audit event", + "type", e.GetType(), + "error", err, + ) + } +} + +// checkAdminCloudAccess validates if the given context has the builtin admin role if cloud feature is enabled. +func checkAdminCloudAccess(authCtx *authz.Context) error { + if modules.GetModules().Features().Cloud && !authz.HasBuiltinRole(*authCtx, string(types.RoleAdmin)) { + return trace.AccessDenied("This Teleport instance is running on Teleport Cloud. "+ + "The %q resource is managed by the Teleport Cloud team. You can use the %q resource to opt-in, "+ + "opt-out or configure update schedules.", + types.KindAutoUpdateVersion, types.KindAutoUpdateConfig) + } + return nil } diff --git a/lib/auth/autoupdate/autoupdatev1/service_test.go b/lib/auth/autoupdate/autoupdatev1/service_test.go index d37e2db72caea..b9c6f6cccbef2 100644 --- a/lib/auth/autoupdate/autoupdatev1/service_test.go +++ b/lib/auth/autoupdate/autoupdatev1/service_test.go @@ -27,8 +27,11 @@ import ( autoupdatev1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/types/autoupdate" + apievents "github.com/gravitational/teleport/api/types/events" "github.com/gravitational/teleport/lib/authz" "github.com/gravitational/teleport/lib/backend/memory" + libevents "github.com/gravitational/teleport/lib/events" + "github.com/gravitational/teleport/lib/events/eventstest" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/services/local" "github.com/gravitational/teleport/lib/tlsca" @@ -43,13 +46,17 @@ func TestAutoUpdateCRUD(t *testing.T) { } } - ctx, localClient, resourceSvc := initSvc(t, "test-cluster") + ctx, localClient, resourceSvc := initSvc(t, "test-cluster", &eventstest.MockRecorderEmitter{}) initialConfig, err := autoupdate.NewAutoUpdateConfig(&autoupdatev1pb.AutoUpdateConfigSpec{ - ToolsAutoupdate: true, + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: "enabled", + }, }) require.NoError(t, err) initialVersion, err := autoupdate.NewAutoUpdateVersion(&autoupdatev1pb.AutoUpdateVersionSpec{ - ToolsVersion: "1.2.3", + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3", + }, }) require.NoError(t, err) @@ -270,6 +277,108 @@ func TestAutoUpdateCRUD(t *testing.T) { } } +func TestAutoUpdateConfigEvents(t *testing.T) { + role := types.RoleSpecV6{ + Allow: types.RoleConditions{Rules: []types.Rule{{ + Resources: []string{types.KindAutoUpdateConfig}, + Verbs: []string{types.VerbList, types.VerbCreate, types.VerbRead, types.VerbUpdate, types.VerbDelete}, + }}}, + } + mockEmitter := &eventstest.MockRecorderEmitter{} + ctx, localClient, service := initSvc(t, "test-cluster", mockEmitter) + localCtx := authorizerForDummyUser(t, ctx, role, localClient) + + config, err := autoupdate.NewAutoUpdateConfig(&autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeEnabled, + }, + }) + require.NoError(t, err) + + _, err = service.CreateAutoUpdateConfig(localCtx, &autoupdatev1pb.CreateAutoUpdateConfigRequest{Config: config}) + require.NoError(t, err) + require.Len(t, mockEmitter.Events(), 1) + require.Equal(t, libevents.AutoUpdateConfigCreateEvent, mockEmitter.LastEvent().GetType()) + require.Equal(t, libevents.AutoUpdateConfigCreateCode, mockEmitter.LastEvent().GetCode()) + require.Equal(t, types.MetaNameAutoUpdateConfig, mockEmitter.LastEvent().(*apievents.AutoUpdateConfigCreate).Name) + mockEmitter.Reset() + + _, err = service.UpdateAutoUpdateConfig(localCtx, &autoupdatev1pb.UpdateAutoUpdateConfigRequest{Config: config}) + require.NoError(t, err) + require.Len(t, mockEmitter.Events(), 1) + require.Equal(t, libevents.AutoUpdateConfigUpdateEvent, mockEmitter.LastEvent().GetType()) + require.Equal(t, libevents.AutoUpdateConfigUpdateCode, mockEmitter.LastEvent().GetCode()) + require.Equal(t, types.MetaNameAutoUpdateConfig, mockEmitter.LastEvent().(*apievents.AutoUpdateConfigUpdate).Name) + mockEmitter.Reset() + + _, err = service.UpsertAutoUpdateConfig(localCtx, &autoupdatev1pb.UpsertAutoUpdateConfigRequest{Config: config}) + require.NoError(t, err) + require.Len(t, mockEmitter.Events(), 1) + require.Equal(t, libevents.AutoUpdateConfigUpdateEvent, mockEmitter.LastEvent().GetType()) + require.Equal(t, libevents.AutoUpdateConfigUpdateCode, mockEmitter.LastEvent().GetCode()) + require.Equal(t, types.MetaNameAutoUpdateConfig, mockEmitter.LastEvent().(*apievents.AutoUpdateConfigUpdate).Name) + mockEmitter.Reset() + + _, err = service.DeleteAutoUpdateConfig(localCtx, &autoupdatev1pb.DeleteAutoUpdateConfigRequest{}) + require.NoError(t, err) + require.Len(t, mockEmitter.Events(), 1) + require.Equal(t, libevents.AutoUpdateConfigDeleteEvent, mockEmitter.LastEvent().GetType()) + require.Equal(t, libevents.AutoUpdateConfigDeleteCode, mockEmitter.LastEvent().GetCode()) + require.Equal(t, types.MetaNameAutoUpdateConfig, mockEmitter.LastEvent().(*apievents.AutoUpdateConfigDelete).Name) + mockEmitter.Reset() +} + +func TestAutoUpdateVersionEvents(t *testing.T) { + role := types.RoleSpecV6{ + Allow: types.RoleConditions{Rules: []types.Rule{{ + Resources: []string{types.KindAutoUpdateVersion}, + Verbs: []string{types.VerbList, types.VerbCreate, types.VerbRead, types.VerbUpdate, types.VerbDelete}, + }}}, + } + mockEmitter := &eventstest.MockRecorderEmitter{} + ctx, localClient, service := initSvc(t, "test-cluster", mockEmitter) + localCtx := authorizerForDummyUser(t, ctx, role, localClient) + + config, err := autoupdate.NewAutoUpdateVersion(&autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3", + }, + }) + require.NoError(t, err) + + _, err = service.CreateAutoUpdateVersion(localCtx, &autoupdatev1pb.CreateAutoUpdateVersionRequest{Version: config}) + require.NoError(t, err) + require.Len(t, mockEmitter.Events(), 1) + require.Equal(t, libevents.AutoUpdateVersionCreateEvent, mockEmitter.LastEvent().GetType()) + require.Equal(t, libevents.AutoUpdateVersionCreateCode, mockEmitter.LastEvent().GetCode()) + require.Equal(t, types.MetaNameAutoUpdateVersion, mockEmitter.LastEvent().(*apievents.AutoUpdateVersionCreate).Name) + mockEmitter.Reset() + + _, err = service.UpdateAutoUpdateVersion(localCtx, &autoupdatev1pb.UpdateAutoUpdateVersionRequest{Version: config}) + require.NoError(t, err) + require.Len(t, mockEmitter.Events(), 1) + require.Equal(t, libevents.AutoUpdateVersionUpdateEvent, mockEmitter.LastEvent().GetType()) + require.Equal(t, libevents.AutoUpdateVersionUpdateCode, mockEmitter.LastEvent().GetCode()) + require.Equal(t, types.MetaNameAutoUpdateVersion, mockEmitter.LastEvent().(*apievents.AutoUpdateVersionUpdate).Name) + mockEmitter.Reset() + + _, err = service.UpsertAutoUpdateVersion(localCtx, &autoupdatev1pb.UpsertAutoUpdateVersionRequest{Version: config}) + require.NoError(t, err) + require.Len(t, mockEmitter.Events(), 1) + require.Equal(t, libevents.AutoUpdateVersionUpdateEvent, mockEmitter.LastEvent().GetType()) + require.Equal(t, libevents.AutoUpdateVersionUpdateCode, mockEmitter.LastEvent().GetCode()) + require.Equal(t, types.MetaNameAutoUpdateVersion, mockEmitter.LastEvent().(*apievents.AutoUpdateVersionUpdate).Name) + mockEmitter.Reset() + + _, err = service.DeleteAutoUpdateVersion(localCtx, &autoupdatev1pb.DeleteAutoUpdateVersionRequest{}) + require.NoError(t, err) + require.Len(t, mockEmitter.Events(), 1) + require.Equal(t, libevents.AutoUpdateVersionDeleteEvent, mockEmitter.LastEvent().GetType()) + require.Equal(t, libevents.AutoUpdateVersionDeleteCode, mockEmitter.LastEvent().GetCode()) + require.Equal(t, types.MetaNameAutoUpdateVersion, mockEmitter.LastEvent().(*apievents.AutoUpdateVersionDelete).Name) + mockEmitter.Reset() +} + func authorizerForDummyUser(t *testing.T, ctx context.Context, roleSpec types.RoleSpecV6, localClient localClient) context.Context { // Create role roleName := "role-" + uuid.NewString() @@ -301,7 +410,7 @@ type localClient interface { services.AutoUpdateService } -func initSvc(t *testing.T, clusterName string) (context.Context, localClient, *Service) { +func initSvc(t *testing.T, clusterName string, emitter apievents.Emitter) (context.Context, localClient, *Service) { ctx := context.Background() backend, err := memory.New(memory.Config{}) require.NoError(t, err) @@ -354,6 +463,7 @@ func initSvc(t *testing.T, clusterName string) (context.Context, localClient, *S Backend: localResourceService, Cache: localResourceService, Authorizer: authorizer, + Emitter: emitter, }) require.NoError(t, err) diff --git a/lib/auth/grpcserver.go b/lib/auth/grpcserver.go index dac27471d4e11..3482ffef13f83 100644 --- a/lib/auth/grpcserver.go +++ b/lib/auth/grpcserver.go @@ -5720,6 +5720,7 @@ func NewGRPCServer(cfg GRPCServerConfig) (*GRPCServer, error) { autoUpdateServiceServer, err := autoupdatev1.NewService(autoupdatev1.ServiceConfig{ Authorizer: cfg.Authorizer, + Emitter: cfg.Emitter, Backend: cfg.AuthServer.Services, Cache: cfg.AuthServer.Cache, }) diff --git a/lib/autoupdate/package_url.go b/lib/autoupdate/package_url.go new file mode 100644 index 0000000000000..9b283c3da59c2 --- /dev/null +++ b/lib/autoupdate/package_url.go @@ -0,0 +1,82 @@ +/* + * Teleport + * Copyright (C) 2025 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package autoupdate + +import ( + "bytes" + "runtime" + "text/template" + + "github.com/gravitational/trace" +) + +// InstallFlags sets flags for the Teleport installation. +type InstallFlags int + +const ( + // FlagEnterprise installs enterprise Teleport. + FlagEnterprise InstallFlags = 1 << iota + // FlagFIPS installs FIPS Teleport + FlagFIPS +) + +const ( + // DefaultBaseURL is CDN URL for downloading official Teleport packages. + DefaultBaseURL = "https://cdn.teleport.dev" + // DefaultPackage is the name of Teleport package. + DefaultPackage = "teleport" + // DefaultCDNURITemplate is the default template for the Teleport CDN download URL. + DefaultCDNURITemplate = `{{ .BaseURL }}/ + {{- if eq .OS "darwin" }} + {{- .Package }}{{ if and .Enterprise (eq .Package "teleport") }}-ent{{ end }}-{{ .Version }}.pkg + {{- else if eq .OS "windows" }} + {{- .Package }}-v{{ .Version }}-{{ .OS }}-amd64-bin.zip + {{- else }} + {{- .Package }}{{ if .Enterprise }}-ent{{ end }}-v{{ .Version }}-{{ .OS }}-{{ .Arch }}{{ if .FIPS }}-fips{{ end }}-bin.tar.gz + {{- end }}` + // BaseURLEnvVar allows to override base URL for the Teleport package URL via env var. + BaseURLEnvVar = "TELEPORT_CDN_BASE_URL" +) + +// MakeURL constructs the package download URL from template, base URL and revision. +func MakeURL(uriTmpl string, baseURL string, pkg string, version string, flags InstallFlags) (string, error) { + tmpl, err := template.New("uri").Parse(uriTmpl) + if err != nil { + return "", trace.Wrap(err) + } + var uriBuf bytes.Buffer + params := struct { + BaseURL, OS, Version, Arch, Package string + FIPS, Enterprise bool + }{ + BaseURL: baseURL, + OS: runtime.GOOS, + Version: version, + Arch: runtime.GOARCH, + FIPS: flags&FlagFIPS != 0, + Enterprise: flags&(FlagEnterprise|FlagFIPS) != 0, + Package: pkg, + } + err = tmpl.Execute(&uriBuf, params) + if err != nil { + return "", trace.Wrap(err) + } + + return uriBuf.String(), nil +} diff --git a/lib/autoupdate/tools/helper.go b/lib/autoupdate/tools/helper.go new file mode 100644 index 0000000000000..f7d3e691b2ef4 --- /dev/null +++ b/lib/autoupdate/tools/helper.go @@ -0,0 +1,127 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package tools + +import ( + "context" + "errors" + "log/slog" + "os" + + "github.com/gravitational/trace" + + "github.com/gravitational/teleport" + "github.com/gravitational/teleport/lib/autoupdate" + stacksignal "github.com/gravitational/teleport/lib/utils/signal" +) + +// Variables might to be overridden during compilation time for integration tests. +var ( + // version is the current version of the Teleport. + version = teleport.Version + // baseURL is CDN URL for downloading official Teleport packages. + baseURL = autoupdate.DefaultBaseURL +) + +// CheckAndUpdateLocal verifies if the TELEPORT_TOOLS_VERSION environment variable +// is set and a version is defined (or disabled by setting it to "off"). The requested +// version is compared with the current client tools version. If they differ, the version +// package is downloaded, extracted to the client tools directory, and re-executed +// with the updated version. +// If $TELEPORT_HOME/bin contains downloaded client tools, it always re-executes +// using the version from the home directory. +func CheckAndUpdateLocal(ctx context.Context, reExecArgs []string) error { + toolsDir, err := Dir() + if err != nil { + slog.WarnContext(ctx, "Client tools update is disabled", "error", err) + return nil + } + + // Overrides default base URL for custom CDN for downloading updates. + if envBaseURL := os.Getenv(autoupdate.BaseURLEnvVar); envBaseURL != "" { + baseURL = envBaseURL + } + + updater := NewUpdater(toolsDir, version, WithBaseURL(baseURL)) + // At process startup, check if a version has already been downloaded to + // $TELEPORT_HOME/bin or if the user has set the TELEPORT_TOOLS_VERSION + // environment variable. If so, re-exec that version of client tools. + toolsVersion, reExec, err := updater.CheckLocal() + if err != nil { + return trace.Wrap(err) + } + if reExec { + return trace.Wrap(updateAndReExec(ctx, updater, toolsVersion, reExecArgs)) + } + + return nil +} + +// CheckAndUpdateRemote verifies client tools version is set for update in cluster +// configuration by making the http request to `webapi/find` endpoint. The requested +// version is compared with the current client tools version. If they differ, the version +// package is downloaded, extracted to the client tools directory, and re-executed +// with the updated version. +// If $TELEPORT_HOME/bin contains downloaded client tools, it always re-executes +// using the version from the home directory. +func CheckAndUpdateRemote(ctx context.Context, proxy string, insecure bool, reExecArgs []string) error { + toolsDir, err := Dir() + if err != nil { + slog.WarnContext(ctx, "Client tools update is disabled", "error", err) + return nil + } + // Overrides default base URL for custom CDN for downloading updates. + if envBaseURL := os.Getenv(autoupdate.BaseURLEnvVar); envBaseURL != "" { + baseURL = envBaseURL + } + + updater := NewUpdater(toolsDir, version, WithBaseURL(baseURL)) + toolsVersion, reExec, err := updater.CheckRemote(ctx, proxy, insecure) + if err != nil { + return trace.Wrap(err) + } + if reExec { + return trace.Wrap(updateAndReExec(ctx, updater, toolsVersion, reExecArgs)) + } + + return nil +} + +func updateAndReExec(ctx context.Context, updater *Updater, toolsVersion string, args []string) error { + ctxUpdate, cancel := stacksignal.GetSignalHandler().NotifyContext(ctx) + defer cancel() + // Download the version of client tools required by the cluster. This + // is required if the user passed in the TELEPORT_TOOLS_VERSION + // explicitly. + err := updater.UpdateWithLock(ctxUpdate, toolsVersion) + if err != nil && !errors.Is(err, context.Canceled) { + return trace.Wrap(err) + } + + // Re-execute client tools with the correct version of client tools. + code, err := updater.Exec(args) + if err != nil && !errors.Is(err, os.ErrNotExist) { + slog.DebugContext(ctx, "Failed to re-exec client tool", "error", err) + os.Exit(code) + } else if err == nil { + os.Exit(code) + } + + return nil +} diff --git a/lib/autoupdate/tools/progress.go b/lib/autoupdate/tools/progress.go new file mode 100644 index 0000000000000..34bbad2ff888d --- /dev/null +++ b/lib/autoupdate/tools/progress.go @@ -0,0 +1,78 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package tools + +import ( + "fmt" + "io" + "strings" + + "github.com/gravitational/trace" +) + +type progressWriter struct { + n int64 + limit int64 + size int + progress int +} + +// newProgressWriter creates progress writer instance and prints empty +// progress bar right after initialisation. +func newProgressWriter(size int) (*progressWriter, func()) { + pw := &progressWriter{size: size} + pw.Print(0) + return pw, func() { + fmt.Print("\n") + } +} + +// Print prints the update progress bar with `n` bricks. +func (w *progressWriter) Print(n int) { + bricks := strings.Repeat("▒", n) + strings.Repeat(" ", w.size-n) + fmt.Print("\rUpdate progress: [" + bricks + "] (Ctrl-C to cancel update)") +} + +func (w *progressWriter) Write(p []byte) (int, error) { + if w.limit == 0 || w.size == 0 { + return len(p), nil + } + + w.n += int64(len(p)) + bricks := int((w.n*100)/w.limit) / w.size + if w.progress != bricks { + w.Print(bricks) + w.progress = bricks + } + + return len(p), nil +} + +// CopyLimit sets the limit of writing bytes to the progress writer and initiate copying process. +func (w *progressWriter) CopyLimit(dst io.Writer, src io.Reader, limit int64) (written int64, err error) { + if limit < 0 { + n, err := io.Copy(dst, io.TeeReader(src, w)) + w.Print(w.size) + return n, trace.Wrap(err) + } + + w.limit = limit + n, err := io.CopyN(dst, io.TeeReader(src, w), limit) + return n, trace.Wrap(err) +} diff --git a/lib/autoupdate/tools/updater.go b/lib/autoupdate/tools/updater.go new file mode 100644 index 0000000000000..d18689b61bb93 --- /dev/null +++ b/lib/autoupdate/tools/updater.go @@ -0,0 +1,444 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package tools + +import ( + "bytes" + "context" + "crypto/sha256" + "crypto/x509" + "encoding/hex" + "fmt" + "io" + "log/slog" + "net/http" + "os" + "os/exec" + "path/filepath" + "regexp" + "runtime" + "syscall" + "time" + + "github.com/coreos/go-semver/semver" + "github.com/google/uuid" + "github.com/gravitational/trace" + + "github.com/gravitational/teleport/api/client/webclient" + "github.com/gravitational/teleport/api/constants" + "github.com/gravitational/teleport/lib/autoupdate" + "github.com/gravitational/teleport/lib/utils" + "github.com/gravitational/teleport/lib/utils/packaging" +) + +const ( + // teleportToolsVersionEnv is environment name for requesting specific version for update. + teleportToolsVersionEnv = "TELEPORT_TOOLS_VERSION" + // reservedFreeDisk is the predefined amount of free disk space (in bytes) required + // to remain available after downloading archives. + reservedFreeDisk = 10 * 1024 * 1024 // 10 Mb + // lockFileName is file used for locking update process in parallel. + lockFileName = ".lock" + // updatePackageSuffix is directory suffix used for package extraction in tools directory. + updatePackageSuffix = "-update-pkg" +) + +var ( + // pattern is template for response on version command for client tools {tsh, tctl}. + pattern = regexp.MustCompile(`(?m)Teleport v(.*) git`) +) + +// Option applies an option value for the Updater. +type Option func(u *Updater) + +// WithBaseURL defines custom base url for the updater. +func WithBaseURL(baseURL string) Option { + return func(u *Updater) { + u.baseURL = baseURL + } +} + +// WithURITemplate defines custom URI template for the updater. +func WithURITemplate(uriTemplate string) Option { + return func(u *Updater) { + u.uriTemplate = uriTemplate + } +} + +// WithClient defines custom http client for the Updater. +func WithClient(client *http.Client) Option { + return func(u *Updater) { + u.client = client + } +} + +// WithTools defines custom list of the tools has to be installed by updater. +func WithTools(tools []string) Option { + return func(u *Updater) { + u.tools = tools + } +} + +// Updater is updater implementation for the client tools auto updates. +type Updater struct { + toolsDir string + localVersion string + tools []string + uriTemplate string + baseURL string + + client *http.Client +} + +// NewUpdater initializes the updater for client tools auto updates. We need to specify the tools directory +// path where we download, extract package archives with the new version, and replace symlinks +// (e.g., `$TELEPORT_HOME/bin`). +// The base URL of the CDN with Teleport packages, the `http.Client` and the list of tools (e.g., `tsh`, `tctl`) +// that must be updated can be customized via options. +func NewUpdater(toolsDir, localVersion string, options ...Option) *Updater { + updater := &Updater{ + tools: DefaultClientTools(), + toolsDir: toolsDir, + localVersion: localVersion, + uriTemplate: autoupdate.DefaultCDNURITemplate, + baseURL: autoupdate.DefaultBaseURL, + client: http.DefaultClient, + } + for _, option := range options { + option(updater) + } + + return updater +} + +// CheckLocal is run at client tool startup and will only perform local checks. +// Returns the version needs to be updated and re-executed, by re-execution flag we +// understand that update and re-execute is required. +func (u *Updater) CheckLocal() (version string, reExec bool, err error) { + // Check if the user has requested a specific version of client tools. + requestedVersion := os.Getenv(teleportToolsVersionEnv) + switch requestedVersion { + // The user has turned off any form of automatic updates. + case "off": + return "", false, nil + // Requested version already the same as client version. + case u.localVersion: + return u.localVersion, false, nil + // No requested version, we continue. + case "": + // Requested version that is not the local one. + default: + if _, err := semver.NewVersion(requestedVersion); err != nil { + return "", false, trace.Wrap(err, "checking that request version is semantic") + } + return requestedVersion, true, nil + } + + // If a version of client tools has already been downloaded to + // tools directory, return that. + toolsVersion, err := CheckToolVersion(u.toolsDir) + if trace.IsNotFound(err) || toolsVersion == u.localVersion { + return u.localVersion, false, nil + } + if err != nil { + return "", false, trace.Wrap(err) + } + + return toolsVersion, true, nil +} + +// CheckRemote first checks the version set by the environment variable. If not set or disabled, +// it checks against the Proxy Service to determine if client tools need updating by requesting +// the `webapi/find` handler, which stores information about the required client tools version to +// operate with this cluster. It returns the semantic version that needs updating and whether +// re-execution is necessary, by re-execution flag we understand that update and re-execute is required. +func (u *Updater) CheckRemote(ctx context.Context, proxyAddr string, insecure bool) (version string, reExec bool, err error) { + // Check if the user has requested a specific version of client tools. + requestedVersion := os.Getenv(teleportToolsVersionEnv) + switch requestedVersion { + // The user has turned off any form of automatic updates. + case "off": + return "", false, nil + // Requested version already the same as client version. + case u.localVersion: + return u.localVersion, false, nil + // No requested version, we continue. + case "": + // Requested version that is not the local one. + default: + if _, err := semver.NewVersion(requestedVersion); err != nil { + return "", false, trace.Wrap(err, "checking that request version is semantic") + } + return requestedVersion, true, nil + } + + certPool, err := x509.SystemCertPool() + if err != nil { + return "", false, trace.Wrap(err) + } + resp, err := webclient.Find(&webclient.Config{ + Context: ctx, + ProxyAddr: proxyAddr, + Pool: certPool, + Timeout: 10 * time.Second, + Insecure: insecure, + }) + if err != nil { + return "", false, trace.Wrap(err) + } + + // If a version of client tools has already been downloaded to + // tools directory, return that. + toolsVersion, err := CheckToolVersion(u.toolsDir) + if err != nil && !trace.IsNotFound(err) { + return "", false, trace.Wrap(err) + } + + switch { + case !resp.AutoUpdate.ToolsAutoUpdate || resp.AutoUpdate.ToolsVersion == "": + if toolsVersion == "" { + return u.localVersion, false, nil + } + case u.localVersion == resp.AutoUpdate.ToolsVersion: + return u.localVersion, false, nil + case resp.AutoUpdate.ToolsVersion != toolsVersion: + return resp.AutoUpdate.ToolsVersion, true, nil + } + + return toolsVersion, true, nil +} + +// UpdateWithLock acquires filesystem lock, downloads requested version package, +// unarchive and replace existing one. +func (u *Updater) UpdateWithLock(ctx context.Context, updateToolsVersion string) (err error) { + // Create tools directory if it does not exist. + if err := os.MkdirAll(u.toolsDir, 0o755); err != nil { + return trace.Wrap(err) + } + // Lock concurrent client tools execution util requested version is updated. + unlock, err := utils.FSWriteLock(filepath.Join(u.toolsDir, lockFileName)) + if err != nil { + return trace.Wrap(err) + } + defer func() { + err = trace.NewAggregate(err, unlock()) + }() + + // If the version of the running binary or the version downloaded to + // tools directory is the same as the requested version of client tools, + // nothing to be done, exit early. + toolsVersion, err := CheckToolVersion(u.toolsDir) + if err != nil && !trace.IsNotFound(err) { + return trace.Wrap(err) + } + if updateToolsVersion == toolsVersion { + return nil + } + + // Download and update client tools in tools directory. + if err := u.Update(ctx, updateToolsVersion); err != nil { + return trace.Wrap(err) + } + + return nil +} + +// Update downloads requested version and replace it with existing one and cleanups the previous downloads +// with defined updater directory suffix. +func (u *Updater) Update(ctx context.Context, toolsVersion string) error { + // Get platform specific download URLs. + packages, err := teleportPackageURLs(u.uriTemplate, u.baseURL, toolsVersion) + if err != nil { + return trace.Wrap(err) + } + + var pkgNames []string + for _, pkg := range packages { + pkgName := fmt.Sprint(uuid.New().String(), updatePackageSuffix) + if err := u.update(ctx, pkg, pkgName); err != nil { + return trace.Wrap(err) + } + pkgNames = append(pkgNames, pkgName) + } + + // Cleanup the tools directory with previously downloaded and un-archived versions. + if err := packaging.RemoveWithSuffix(u.toolsDir, updatePackageSuffix, pkgNames); err != nil { + slog.WarnContext(ctx, "failed to clean up tools directory", "error", err) + } + + return nil +} + +// update downloads the archive and validate against the hash. Download to a +// temporary path within tools directory. +func (u *Updater) update(ctx context.Context, pkg packageURL, pkgName string) error { + f, err := os.CreateTemp("", "teleport-") + if err != nil { + return trace.Wrap(err) + } + defer func() { + _ = f.Close() + if err := os.Remove(f.Name()); err != nil { + slog.WarnContext(ctx, "failed to remove temporary archive file", "error", err) + } + }() + + archiveHash, err := u.downloadArchive(ctx, pkg.Archive, f) + if pkg.Optional && trace.IsNotFound(err) { + return nil + } + if err != nil { + return trace.Wrap(err) + } + + hash, err := u.downloadHash(ctx, pkg.Hash) + if pkg.Optional && trace.IsNotFound(err) { + return nil + } + if err != nil { + return trace.Wrap(err) + } + + if !bytes.Equal(archiveHash, hash) { + return trace.BadParameter("hash of archive does not match downloaded archive") + } + + extractDir := filepath.Join(u.toolsDir, pkgName) + if runtime.GOOS != constants.DarwinOS { + if err := os.Mkdir(extractDir, 0o755); err != nil { + return trace.Wrap(err) + } + } + + // Perform atomic replace so concurrent exec do not fail. + if err := packaging.ReplaceToolsBinaries(u.toolsDir, f.Name(), extractDir, u.tools); err != nil { + return trace.Wrap(err) + } + + return nil +} + +// Exec re-executes tool command with same arguments and environ variables. +func (u *Updater) Exec(args []string) (int, error) { + path, err := toolName(u.toolsDir) + if err != nil { + return 0, trace.Wrap(err) + } + // To prevent re-execution loop we have to disable update logic for re-execution, + // by unsetting current tools version env variable and setting it to "off". + if err := os.Unsetenv(teleportToolsVersionEnv); err != nil { + return 0, trace.Wrap(err) + } + + env := os.Environ() + executablePath, err := os.Executable() + if err != nil { + return 0, trace.Wrap(err) + } + if path == executablePath { + env = append(env, teleportToolsVersionEnv+"=off") + } + + if runtime.GOOS == constants.WindowsOS { + cmd := exec.Command(path, args...) + cmd.Env = env + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return 0, trace.Wrap(err) + } + + return cmd.ProcessState.ExitCode(), nil + } + + if err := syscall.Exec(path, append([]string{path}, args...), env); err != nil { + return 0, trace.Wrap(err) + } + + return 0, nil +} + +// downloadHash downloads the hash file `.sha256` for package checksum validation and return the hash sum. +func (u *Updater) downloadHash(ctx context.Context, url string) ([]byte, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return nil, trace.Wrap(err) + } + resp, err := u.client.Do(req) + if err != nil { + return nil, trace.Wrap(err) + } + defer resp.Body.Close() + if resp.StatusCode == http.StatusNotFound { + return nil, trace.NotFound("hash file is not found: %q", url) + } + if resp.StatusCode != http.StatusOK { + return nil, trace.BadParameter("bad status when downloading archive hash: %v", resp.StatusCode) + } + + var buf bytes.Buffer + _, err = io.CopyN(&buf, resp.Body, sha256.Size*2) // SHA bytes to hex + if err != nil { + return nil, trace.Wrap(err) + } + hexBytes, err := hex.DecodeString(buf.String()) + if err != nil { + return nil, trace.Wrap(err) + } + + return hexBytes, nil +} + +// downloadArchive downloads the archive package by `url` and writes content to the writer interface, +// return calculated sha256 hash sum of the content. +func (u *Updater) downloadArchive(ctx context.Context, url string, f io.Writer) ([]byte, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return nil, trace.Wrap(err) + } + resp, err := u.client.Do(req) + if err != nil { + return nil, trace.Wrap(err) + } + defer resp.Body.Close() + if resp.StatusCode == http.StatusNotFound { + return nil, trace.NotFound("archive file is not found: %v", resp.StatusCode) + } + if resp.StatusCode != http.StatusOK { + return nil, trace.BadParameter("bad status when downloading archive: %v", resp.StatusCode) + } + pw, finish := newProgressWriter(10) + defer finish() + + if resp.ContentLength != -1 { + if err := checkFreeSpace(u.toolsDir, uint64(resp.ContentLength)); err != nil { + return nil, trace.Wrap(err) + } + } + h := sha256.New() + // It is a little inefficient to download the file to disk and then re-load + // it into memory to unarchive later, but this is safer as it allows client + // tools to validate the hash before trying to operate on the archive. + if _, err := pw.CopyLimit(f, io.TeeReader(resp.Body, h), resp.ContentLength); err != nil { + return nil, trace.Wrap(err) + } + + return h.Sum(nil), nil +} diff --git a/lib/autoupdate/tools/utils.go b/lib/autoupdate/tools/utils.go new file mode 100644 index 0000000000000..d9624b4761993 --- /dev/null +++ b/lib/autoupdate/tools/utils.go @@ -0,0 +1,184 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package tools + +import ( + "bufio" + "bytes" + "context" + "errors" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "time" + + "github.com/coreos/go-semver/semver" + "github.com/gravitational/trace" + + "github.com/gravitational/teleport/api/constants" + "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/autoupdate" + "github.com/gravitational/teleport/lib/modules" + "github.com/gravitational/teleport/lib/utils" +) + +// Dir returns the path to client tools in $TELEPORT_HOME/bin. +func Dir() (string, error) { + home := os.Getenv(types.HomeEnvVar) + if home == "" { + var err error + home, err = os.UserHomeDir() + if err != nil { + return "", trace.Wrap(err) + } + } + + return filepath.Join(home, ".tsh", "bin"), nil +} + +// DefaultClientTools list of the client tools needs to be updated by default. +func DefaultClientTools() []string { + switch runtime.GOOS { + case constants.WindowsOS: + return []string{"tsh.exe", "tctl.exe"} + default: + return []string{"tsh", "tctl"} + } +} + +// CheckToolVersion returns current installed client tools version, must return NotFoundError if +// the client tools is not found in tools directory. +func CheckToolVersion(toolsDir string) (string, error) { + // Find the path to the current executable. + path, err := toolName(toolsDir) + if err != nil { + return "", trace.Wrap(err) + } + if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) { + return "", trace.NotFound("autoupdate tool not found in %q", toolsDir) + } else if err != nil { + return "", trace.Wrap(err) + } + + // Set a timeout to not let "{tsh, tctl} version" block forever. Allow up + // to 10 seconds because sometimes MDM tools like Jamf cause a lot of + // latency in launching binaries. + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Execute "{tsh, tctl} version" and pass in TELEPORT_TOOLS_VERSION=off to + // turn off all automatic updates code paths to prevent any recursion. + command := exec.CommandContext(ctx, path, "version") + command.Env = []string{teleportToolsVersionEnv + "=off"} + output, err := command.Output() + if err != nil { + return "", trace.WrapWithMessage(err, "failed to determine version of %q tool", path) + } + + // The output for "{tsh, tctl} version" can be multiple lines. Find the + // actual version line and extract the version. + scanner := bufio.NewScanner(bytes.NewReader(output)) + for scanner.Scan() { + line := scanner.Text() + + if !strings.HasPrefix(line, "Teleport") { + continue + } + + matches := pattern.FindStringSubmatch(line) + if len(matches) != 2 { + return "", trace.BadParameter("invalid version line: %v", line) + } + version, err := semver.NewVersion(matches[1]) + if err != nil { + return "", trace.Wrap(err) + } + return version.String(), nil + } + + return "", trace.BadParameter("unable to determine version") +} + +// packageURL defines URLs to the archive and their archive sha256 hash file, and marks +// if this package is optional, for such case download needs to be ignored if package +// not found in CDN. +type packageURL struct { + Archive string + Hash string + Optional bool +} + +// teleportPackageURLs returns the URL for the Teleport archive to download. +func teleportPackageURLs(uriTmpl string, baseURL, version string) ([]packageURL, error) { + var flags autoupdate.InstallFlags + m := modules.GetModules() + if m.IsBoringBinary() { + flags |= autoupdate.FlagFIPS + } + if m.BuildType() == modules.BuildEnterprise || m.IsBoringBinary() { + flags |= autoupdate.FlagEnterprise + } + + teleportURL, err := autoupdate.MakeURL(uriTmpl, baseURL, autoupdate.DefaultPackage, version, flags) + if err != nil { + return nil, trace.Wrap(err) + } + if runtime.GOOS == constants.DarwinOS { + tshURL, err := autoupdate.MakeURL(uriTmpl, baseURL, "tsh", version, flags) + if err != nil { + return nil, trace.Wrap(err) + } + + return []packageURL{ + {Archive: teleportURL, Hash: teleportURL + ".sha256"}, + {Archive: tshURL, Hash: tshURL + ".sha256", Optional: true}, + }, nil + } + + return []packageURL{ + {Archive: teleportURL, Hash: teleportURL + ".sha256"}, + }, nil +} + +// toolName returns the path to {tsh, tctl} for the executable that started +// the current process. +func toolName(toolsDir string) (string, error) { + executablePath, err := os.Executable() + if err != nil { + return "", trace.Wrap(err) + } + + return filepath.Join(toolsDir, filepath.Base(executablePath)), nil +} + +// checkFreeSpace verifies that we have enough requested space at specific directory. +func checkFreeSpace(path string, requested uint64) error { + free, err := utils.FreeDiskWithReserve(path, reservedFreeDisk) + if err != nil { + return trace.Errorf("failed to calculate free disk in %q: %v", path, err) + } + // Bail if there's not enough free disk space at the target. + if requested > free { + return trace.Errorf("%q needs %d additional bytes of disk space", path, requested-free) + } + + return nil +} diff --git a/lib/cache/cache_test.go b/lib/cache/cache_test.go index 57706bacca209..9f1384bb27d42 100644 --- a/lib/cache/cache_test.go +++ b/lib/cache/cache_test.go @@ -3563,7 +3563,9 @@ func newAutoUpdateConfig(t *testing.T) *autoupdate.AutoUpdateConfig { t.Helper() r, err := update.NewAutoUpdateConfig(&autoupdate.AutoUpdateConfigSpec{ - ToolsAutoupdate: true, + Tools: &autoupdate.AutoUpdateConfigSpecTools{ + Mode: update.ToolsUpdateModeEnabled, + }, }) require.NoError(t, err) return r @@ -3573,7 +3575,9 @@ func newAutoUpdateVersion(t *testing.T) *autoupdate.AutoUpdateVersion { t.Helper() r, err := update.NewAutoUpdateVersion(&autoupdate.AutoUpdateVersionSpec{ - ToolsVersion: "1.2.3", + Tools: &autoupdate.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3", + }, }) require.NoError(t, err) return r diff --git a/lib/client/api.go b/lib/client/api.go index 70ed835477379..b445a35497ed0 100644 --- a/lib/client/api.go +++ b/lib/client/api.go @@ -69,6 +69,7 @@ import ( "github.com/gravitational/teleport/lib/auth/touchid" wancli "github.com/gravitational/teleport/lib/auth/webauthncli" "github.com/gravitational/teleport/lib/authz" + "github.com/gravitational/teleport/lib/autoupdate/tools" "github.com/gravitational/teleport/lib/client/mfa" "github.com/gravitational/teleport/lib/client/terminal" "github.com/gravitational/teleport/lib/defaults" @@ -636,6 +637,10 @@ func RetryWithRelogin(ctx context.Context, tc *TeleportClient, fn func() error, return trace.Wrap(err) } + if err := tools.CheckAndUpdateRemote(ctx, tc.WebProxyAddr, tc.InsecureSkipVerify, os.Args[1:]); err != nil { + return trace.Wrap(err) + } + return fn() } diff --git a/lib/client/keystore.go b/lib/client/keystore.go index fa17306b29089..2f45e2d50827b 100644 --- a/lib/client/keystore.go +++ b/lib/client/keystore.go @@ -52,6 +52,10 @@ const ( // tshAzureDirName is the name of the directory containing the // az cli app-specific profiles. tshAzureDirName = "azure" + + // tshBin is the name of the directory containing the + // updated binaries of client tools. + tshBin = "bin" ) // KeyStore is a storage interface for client session keys and certificates. @@ -280,13 +284,11 @@ func (fs *FSKeyStore) DeleteKeys() error { if err != nil { return trace.ConvertSystemError(err) } + ignoreDirs := map[string]struct{}{tshConfigFileName: {}, tshAzureDirName: {}, tshBin: {}} for _, file := range files { - // Don't delete 'config' and 'azure' directories. + // Don't delete 'config', 'azure' and 'bin' directories. // TODO: this is hackish and really shouldn't be needed, but fs.KeyDir is `~/.tsh` while it probably should be `~/.tsh/keys` instead. - if file.IsDir() && file.Name() == tshConfigFileName { - continue - } - if file.IsDir() && file.Name() == tshAzureDirName { + if _, ok := ignoreDirs[file.Name()]; ok && file.IsDir() { continue } if file.IsDir() { diff --git a/lib/client/keystore_test.go b/lib/client/keystore_test.go index b7258ceb573b3..6691aa0a0ac41 100644 --- a/lib/client/keystore_test.go +++ b/lib/client/keystore_test.go @@ -274,17 +274,31 @@ func TestAddKey_withoutSSHCert(t *testing.T) { require.Len(t, keyCopy.DBTLSCerts, 1) } -func TestConfigDirNotDeleted(t *testing.T) { +func TestProtectedDirsNotDeleted(t *testing.T) { t.Parallel() auth := newTestAuthority(t) keyStore := newTestFSKeyStore(t) idx := KeyIndex{"host.a", "bob", "root"} keyStore.AddKey(auth.makeSignedKey(t, idx, false)) + configPath := filepath.Join(keyStore.KeyDir, "config") require.NoError(t, os.Mkdir(configPath, 0700)) + + azurePath := filepath.Join(keyStore.KeyDir, "azure") + require.NoError(t, os.Mkdir(azurePath, 0700)) + + binPath := filepath.Join(keyStore.KeyDir, "bin") + require.NoError(t, os.Mkdir(binPath, 0700)) + + testPath := filepath.Join(keyStore.KeyDir, "test") + require.NoError(t, os.Mkdir(testPath, 0700)) + require.NoError(t, keyStore.DeleteKeys()) require.DirExists(t, configPath) + require.DirExists(t, azurePath) + require.DirExists(t, binPath) + require.NoDirExists(t, testPath) require.NoDirExists(t, filepath.Join(keyStore.KeyDir, "keys")) } diff --git a/lib/events/api.go b/lib/events/api.go index 8d22b3d09022c..44ea7ea64de7d 100644 --- a/lib/events/api.go +++ b/lib/events/api.go @@ -715,6 +715,20 @@ const ( IntegrationUpdateEvent = "integration.update" // IntegrationDeleteEvent is emitted when an integration resource is deleted. IntegrationDeleteEvent = "integration.delete" + + // AutoUpdateConfigCreateEvent is emitted when a AutoUpdateConfig resource is created. + AutoUpdateConfigCreateEvent = "auto_update_config.create" + // AutoUpdateConfigUpdateEvent is emitted when a AutoUpdateConfig resource is updated. + AutoUpdateConfigUpdateEvent = "auto_update_config.update" + // AutoUpdateConfigDeleteEvent is emitted when a AutoUpdateConfig resource is deleted. + AutoUpdateConfigDeleteEvent = "auto_update_config.delete" + + // AutoUpdateVersionCreateEvent is emitted when a AutoUpdateVersion resource is created. + AutoUpdateVersionCreateEvent = "auto_update_version.create" + // AutoUpdateVersionUpdateEvent is emitted when a AutoUpdateVersion resource is updated. + AutoUpdateVersionUpdateEvent = "auto_update_version.update" + // AutoUpdateVersionDeleteEvent is emitted when a AutoUpdateVersion resource is deleted. + AutoUpdateVersionDeleteEvent = "auto_update_version.delete" ) // Add an entry to eventsMap in lib/events/events_test.go when you add diff --git a/lib/events/codes.go b/lib/events/codes.go index 2953c89d9d5bc..e96c5440ef26c 100644 --- a/lib/events/codes.go +++ b/lib/events/codes.go @@ -555,6 +555,20 @@ const ( // IntegrationDeleteCode is the integration resource delete event code. IntegrationDeleteCode = "IG003I" + // AutoUpdateConfigCreateCode is the auto update config create event code. + AutoUpdateConfigCreateCode = "AUC001I" + // AutoUpdateConfigUpdateCode is the auto update config update event code. + AutoUpdateConfigUpdateCode = "AUC002I" + // AutoUpdateConfigDeleteCode is the auto update config delete event code. + AutoUpdateConfigDeleteCode = "AUC003I" + + // AutoUpdateVersionCreateCode is the auto update version create event code. + AutoUpdateVersionCreateCode = "AUV001I" + // AutoUpdateVersionUpdateCode is the auto update version update event code. + AutoUpdateVersionUpdateCode = "AUV002I" + // AutoUpdateVersionDeleteCode is the auto update version delete event code. + AutoUpdateVersionDeleteCode = "AUV003I" + // UnknownCode is used when an event of unknown type is encountered. UnknownCode = apievents.UnknownCode ) diff --git a/lib/events/dynamic.go b/lib/events/dynamic.go index 788ca2700dd5c..f6829c2532e0d 100644 --- a/lib/events/dynamic.go +++ b/lib/events/dynamic.go @@ -366,6 +366,19 @@ func FromEventFields(fields EventFields) (events.AuditEvent, error) { case IntegrationDeleteEvent: e = &events.IntegrationDelete{} + case AutoUpdateConfigCreateEvent: + e = &events.AutoUpdateConfigCreate{} + case AutoUpdateConfigUpdateEvent: + e = &events.AutoUpdateConfigUpdate{} + case AutoUpdateConfigDeleteEvent: + e = &events.AutoUpdateConfigDelete{} + + case AutoUpdateVersionCreateEvent: + e = &events.AutoUpdateVersionCreate{} + case AutoUpdateVersionUpdateEvent: + e = &events.AutoUpdateVersionUpdate{} + case AutoUpdateVersionDeleteEvent: + e = &events.AutoUpdateVersionDelete{} default: log.Errorf("Attempted to convert dynamic event of unknown type %q into protobuf event.", eventType) unknown := &events.Unknown{} diff --git a/lib/events/events_test.go b/lib/events/events_test.go index 71ce27164fd22..56b7143abdc71 100644 --- a/lib/events/events_test.go +++ b/lib/events/events_test.go @@ -194,6 +194,12 @@ var eventsMap = map[string]apievents.AuditEvent{ IntegrationCreateEvent: &apievents.IntegrationCreate{}, IntegrationUpdateEvent: &apievents.IntegrationUpdate{}, IntegrationDeleteEvent: &apievents.IntegrationDelete{}, + AutoUpdateConfigCreateEvent: &apievents.AutoUpdateConfigCreate{}, + AutoUpdateConfigUpdateEvent: &apievents.AutoUpdateConfigUpdate{}, + AutoUpdateConfigDeleteEvent: &apievents.AutoUpdateConfigDelete{}, + AutoUpdateVersionCreateEvent: &apievents.AutoUpdateVersionCreate{}, + AutoUpdateVersionUpdateEvent: &apievents.AutoUpdateVersionUpdate{}, + AutoUpdateVersionDeleteEvent: &apievents.AutoUpdateVersionDelete{}, } // TestJSON tests JSON marshal events diff --git a/lib/services/local/autoupdate_test.go b/lib/services/local/autoupdate_test.go index d46a40b75647b..92ec037fbc5a6 100644 --- a/lib/services/local/autoupdate_test.go +++ b/lib/services/local/autoupdate_test.go @@ -51,7 +51,11 @@ func TestAutoUpdateServiceConfigCRUD(t *testing.T) { Kind: types.KindAutoUpdateConfig, Version: types.V1, Metadata: &headerv1.Metadata{Name: types.MetaNameAutoUpdateConfig}, - Spec: &autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: true}, + Spec: &autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeEnabled, + }, + }, } created, err := service.CreateAutoUpdateConfig(ctx, config) @@ -71,10 +75,12 @@ func TestAutoUpdateServiceConfigCRUD(t *testing.T) { require.Empty(t, diff) require.Equal(t, created.GetMetadata().GetRevision(), got.GetMetadata().GetRevision()) - config.Spec.ToolsAutoupdate = false + config.Spec.Tools = &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeDisabled, + } updated, err := service.UpdateAutoUpdateConfig(ctx, config) require.NoError(t, err) - require.NotEqual(t, got.GetSpec().GetToolsAutoupdate(), updated.GetSpec().GetToolsAutoupdate()) + require.NotEqual(t, got.GetSpec().GetTools(), updated.GetSpec().GetTools()) _, err = service.UpsertAutoUpdateConfig(ctx, config) require.NoError(t, err) @@ -106,7 +112,11 @@ func TestAutoUpdateServiceVersionCRUD(t *testing.T) { Kind: types.KindAutoUpdateVersion, Version: types.V1, Metadata: &headerv1.Metadata{Name: types.MetaNameAutoUpdateVersion}, - Spec: &autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "1.2.3"}, + Spec: &autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3", + }, + }, } created, err := service.CreateAutoUpdateVersion(ctx, version) @@ -126,10 +136,12 @@ func TestAutoUpdateServiceVersionCRUD(t *testing.T) { require.Empty(t, diff) require.Equal(t, created.GetMetadata().GetRevision(), got.GetMetadata().GetRevision()) - version.Spec.ToolsVersion = "3.2.1" + version.Spec.Tools = &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "3.2.1", + } updated, err := service.UpdateAutoUpdateVersion(ctx, version) require.NoError(t, err) - require.NotEqual(t, got.GetSpec().GetToolsVersion(), updated.GetSpec().GetToolsVersion()) + require.NotEqual(t, got.GetSpec().GetTools().GetTargetVersion(), updated.GetSpec().GetTools().GetTargetVersion()) _, err = service.UpsertAutoUpdateVersion(ctx, version) require.NoError(t, err) @@ -161,7 +173,11 @@ func TestAutoUpdateServiceInvalidNameCreate(t *testing.T) { Kind: types.KindAutoUpdateConfig, Version: types.V1, Metadata: &headerv1.Metadata{Name: "invalid-auto-update-config-name"}, - Spec: &autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: true}, + Spec: &autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeEnabled, + }, + }, } createdConfig, err := service.CreateAutoUpdateConfig(ctx, config) @@ -172,7 +188,11 @@ func TestAutoUpdateServiceInvalidNameCreate(t *testing.T) { Kind: types.KindAutoUpdateVersion, Version: types.V1, Metadata: &headerv1.Metadata{Name: "invalid-auto-update-version-name"}, - Spec: &autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "1.2.3"}, + Spec: &autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3", + }, + }, } createdVersion, err := service.CreateAutoUpdateVersion(ctx, version) @@ -194,7 +214,11 @@ func TestAutoUpdateServiceInvalidNameUpdate(t *testing.T) { ctx := context.Background() // Validate the config update restriction. - config, err := autoupdate.NewAutoUpdateConfig(&autoupdatev1pb.AutoUpdateConfigSpec{ToolsAutoupdate: true}) + config, err := autoupdate.NewAutoUpdateConfig(&autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeEnabled, + }, + }) require.NoError(t, err) createdConfig, err := service.UpsertAutoUpdateConfig(ctx, config) @@ -207,7 +231,11 @@ func TestAutoUpdateServiceInvalidNameUpdate(t *testing.T) { require.Nil(t, createdConfig) // Validate the version update restriction. - version, err := autoupdate.NewAutoUpdateVersion(&autoupdatev1pb.AutoUpdateVersionSpec{ToolsVersion: "1.2.3"}) + version, err := autoupdate.NewAutoUpdateVersion(&autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3", + }, + }) require.NoError(t, err) createdVersion, err := service.UpsertAutoUpdateVersion(ctx, version) diff --git a/lib/services/presets.go b/lib/services/presets.go index 1a4e4777fe2dc..56e4327c62673 100644 --- a/lib/services/presets.go +++ b/lib/services/presets.go @@ -169,6 +169,8 @@ func NewPresetEditorRole() types.Role { types.NewRule(types.KindAuditQuery, append(RW(), types.VerbUse)), types.NewRule(types.KindAccessGraph, RW()), types.NewRule(types.KindServerInfo, RW()), + types.NewRule(types.KindAutoUpdateVersion, RW()), + types.NewRule(types.KindAutoUpdateConfig, RW()), }, }, }, diff --git a/lib/utils/disk.go b/lib/utils/disk.go index 95ec98dd0f405..f86bbb100204d 100644 --- a/lib/utils/disk.go +++ b/lib/utils/disk.go @@ -44,6 +44,21 @@ func PercentUsed(path string) (float64, error) { return Round(ratio * 100), nil } +// FreeDiskWithReserve returns the available disk space (in bytes) on the disk at dir, minus `reservedFreeDisk`. +func FreeDiskWithReserve(dir string, reservedFreeDisk uint64) (uint64, error) { + var stat syscall.Statfs_t + err := syscall.Statfs(dir, &stat) + if err != nil { + return 0, trace.Wrap(err) + } + //nolint:unconvert // The cast is only necessary for linux platform. + avail := uint64(stat.Bavail) * uint64(stat.Bsize) + if reservedFreeDisk > avail { + return 0, trace.Errorf("no free space left") + } + return avail - reservedFreeDisk, nil +} + // CanUserWriteTo attempts to check if a user has write access to certain path. // It also works around the program being run as root and tries to check // the permissions of the user who executed the program as root. diff --git a/lib/utils/disk_windows.go b/lib/utils/disk_windows.go index 3abc892f2c1e8..26e11d05d6667 100644 --- a/lib/utils/disk_windows.go +++ b/lib/utils/disk_windows.go @@ -19,13 +19,29 @@ limitations under the License. package utils -import "github.com/gravitational/trace" +import ( + "github.com/gravitational/trace" + "golang.org/x/sys/windows" +) // PercentUsed is not supported on Windows. func PercentUsed(path string) (float64, error) { return 0.0, trace.NotImplemented("disk usage not supported on Windows") } +// FreeDiskWithReserve returns the available disk space (in bytes) on the disk at dir, minus `reservedFreeDisk`. +func FreeDiskWithReserve(dir string, reservedFreeDisk uint64) (uint64, error) { + var avail uint64 + err := windows.GetDiskFreeSpaceEx(windows.StringToUTF16Ptr(dir), &avail, nil, nil) + if err != nil { + return 0, trace.Wrap(err) + } + if reservedFreeDisk > avail { + return 0, trace.Errorf("no free space left") + } + return avail - reservedFreeDisk, nil +} + // CanUserWriteTo is not supported on Windows. func CanUserWriteTo(path string) (bool, error) { return false, trace.NotImplemented("path permission checking is not supported on Windows") diff --git a/lib/utils/fs.go b/lib/utils/fs.go index 49254628f0ac4..7cbbf2fb64470 100644 --- a/lib/utils/fs.go +++ b/lib/utils/fs.go @@ -420,15 +420,19 @@ func RecursiveChown(dir string, uid, gid int) error { return nil } -func CopyFile(src, dest string, perm os.FileMode) error { +func CopyFile(src, dest string, perm os.FileMode) (err error) { srcFile, err := os.Open(src) if err != nil { return trace.ConvertSystemError(err) } + defer srcFile.Close() destFile, err := os.OpenFile(dest, os.O_RDWR|os.O_CREATE|os.O_TRUNC, perm) if err != nil { return trace.ConvertSystemError(err) } + defer func() { + err = trace.NewAggregate(err, trace.Wrap(destFile.Close())) + }() _, err = destFile.ReadFrom(srcFile) if err != nil { return trace.ConvertSystemError(err) diff --git a/lib/utils/packaging/unarchive.go b/lib/utils/packaging/unarchive.go new file mode 100644 index 0000000000000..ed81a4b4b2d24 --- /dev/null +++ b/lib/utils/packaging/unarchive.go @@ -0,0 +1,160 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package packaging + +import ( + "archive/zip" + "io" + "os" + "path/filepath" + "slices" + "strings" + + "github.com/gravitational/trace" + + "github.com/gravitational/teleport/lib/utils" +) + +const ( + // reservedFreeDisk is the predefined amount of free disk space (in bytes) required + // to remain available after extracting Teleport binaries. + reservedFreeDisk = 10 * 1024 * 1024 +) + +// RemoveWithSuffix removes all that matches the provided suffix, except for file or directory with `skipName`. +func RemoveWithSuffix(dir, suffix string, skipNames []string) error { + var removePaths []string + err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return trace.Wrap(err) + } + if slices.Contains(skipNames, info.Name()) { + return nil + } + if !strings.HasSuffix(info.Name(), suffix) { + return nil + } + removePaths = append(removePaths, path) + if info.IsDir() { + return filepath.SkipDir + } + return nil + }) + if err != nil { + return trace.Wrap(err) + } + var aggErr []error + for _, path := range removePaths { + if err := os.RemoveAll(path); err != nil { + aggErr = append(aggErr, err) + } + } + return trace.NewAggregate(aggErr...) +} + +// replaceZip un-archives the Teleport package in .zip format, iterates through +// the compressed content, and ignores everything not matching the binaries specified +// in the execNames argument. The data is extracted to extractDir, and symlinks are created +// in toolsDir pointing to the extractDir path with binaries. +func replaceZip(toolsDir string, archivePath string, extractDir string, execNames []string) error { + f, err := os.Open(archivePath) + if err != nil { + return trace.Wrap(err) + } + defer f.Close() + + fi, err := f.Stat() + if err != nil { + return trace.Wrap(err) + } + zipReader, err := zip.NewReader(f, fi.Size()) + if err != nil { + return trace.Wrap(err) + } + + var totalSize uint64 = 0 + for _, zipFile := range zipReader.File { + baseName := filepath.Base(zipFile.Name) + // Skip over any files in the archive that are not defined execNames. + if !slices.ContainsFunc(execNames, func(s string) bool { + return baseName == s + }) { + continue + } + totalSize += zipFile.UncompressedSize64 + } + // Verify that we have enough space for uncompressed zipFile. + if err := checkFreeSpace(extractDir, totalSize); err != nil { + return trace.Wrap(err) + } + + for _, zipFile := range zipReader.File { + baseName := filepath.Base(zipFile.Name) + // Skip over any files in the archive that are not defined execNames. + if !slices.Contains(execNames, baseName) { + continue + } + + if err := func(zipFile *zip.File) error { + file, err := zipFile.Open() + if err != nil { + return trace.Wrap(err) + } + defer file.Close() + + dest := filepath.Join(extractDir, baseName) + destFile, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o755) + if err != nil { + return trace.Wrap(err) + } + defer destFile.Close() + + if _, err := io.Copy(destFile, file); err != nil { + return trace.Wrap(err) + } + appPath := filepath.Join(toolsDir, baseName) + // For the Windows build, we need to copy the binary to perform updates without requiring + // administrative access, which would otherwise be needed for creating symlinks. + // Since symlinks are not used on the Windows platform, there's no need to remove appPath + // before copying the new binary — it will simply be replaced. + if err := utils.CopyFile(dest, appPath, 0o755); err != nil { + return trace.Wrap(err) + } + return trace.Wrap(destFile.Close()) + }(zipFile); err != nil { + return trace.Wrap(err) + } + } + + return nil +} + +// checkFreeSpace verifies that we have enough requested space (in bytes) at specific directory. +func checkFreeSpace(path string, requested uint64) error { + free, err := utils.FreeDiskWithReserve(path, reservedFreeDisk) + if err != nil { + return trace.Errorf("failed to calculate free disk in %q: %v", path, err) + } + // Bail if there's not enough free disk space at the target. + if requested > free { + return trace.Errorf("%q needs %d additional bytes of disk space", path, requested-free) + } + + return nil +} diff --git a/lib/utils/packaging/unarchive_test.go b/lib/utils/packaging/unarchive_test.go new file mode 100644 index 0000000000000..b124b603b0fd5 --- /dev/null +++ b/lib/utils/packaging/unarchive_test.go @@ -0,0 +1,147 @@ +//go:build !windows + +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package packaging + +import ( + "context" + "os" + "path/filepath" + "runtime" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/integration/helpers/archive" +) + +// TestPackaging verifies un-archiving of all supported teleport package formats. +func TestPackaging(t *testing.T) { + script := "#!/bin/sh\necho test" + + sourceDir, err := os.MkdirTemp(os.TempDir(), "source") + require.NoError(t, err) + + toolsDir, err := os.MkdirTemp(os.TempDir(), "dest") + require.NoError(t, err) + + extractDir, err := os.MkdirTemp(toolsDir, "extract") + require.NoError(t, err) + + t.Cleanup(func() { + require.NoError(t, os.RemoveAll(extractDir)) + require.NoError(t, os.RemoveAll(sourceDir)) + require.NoError(t, os.RemoveAll(toolsDir)) + }) + + // Create test script for packaging in relative path `teleport\bin` to ensure that + // binaries going to be identified and extracted flatten to `extractDir`. + binPath := filepath.Join(sourceDir, "teleport", "bin") + require.NoError(t, os.MkdirAll(binPath, 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(binPath, "tsh"), []byte(script), 0o755)) + require.NoError(t, os.WriteFile(filepath.Join(binPath, "tctl"), []byte(script), 0o755)) + + ctx := context.Background() + + t.Run("tar.gz", func(t *testing.T) { + archivePath := filepath.Join(toolsDir, "tsh.tar.gz") + err = archive.CompressDirToTarGzFile(ctx, sourceDir, archivePath) + require.NoError(t, err) + require.FileExists(t, archivePath, "archive not created") + + // For the .tar.gz format we extract app by app to check that content discard is not required. + err = replaceTarGz(toolsDir, archivePath, extractDir, []string{"tctl"}) + require.NoError(t, err) + err = replaceTarGz(toolsDir, archivePath, extractDir, []string{"tsh"}) + require.NoError(t, err) + assert.FileExists(t, filepath.Join(toolsDir, "tsh"), "script not created") + assert.FileExists(t, filepath.Join(toolsDir, "tctl"), "script not created") + + data, err := os.ReadFile(filepath.Join(toolsDir, "tsh")) + require.NoError(t, err) + assert.Equal(t, script, string(data)) + }) + + t.Run("pkg", func(t *testing.T) { + if runtime.GOOS != "darwin" { + t.Skip("unsupported platform") + } + archivePath := filepath.Join(toolsDir, "tsh.pkg") + err = archive.CompressDirToPkgFile(ctx, sourceDir, archivePath, "com.example.pkgtest") + require.NoError(t, err) + require.FileExists(t, archivePath, "archive not created") + + err = replacePkg(toolsDir, archivePath, filepath.Join(extractDir, "apps"), []string{"tsh", "tctl"}) + require.NoError(t, err) + assert.FileExists(t, filepath.Join(toolsDir, "tsh"), "script not created") + assert.FileExists(t, filepath.Join(toolsDir, "tctl"), "script not created") + + data, err := os.ReadFile(filepath.Join(toolsDir, "tsh")) + require.NoError(t, err) + assert.Equal(t, script, string(data)) + }) + + t.Run("zip", func(t *testing.T) { + archivePath := filepath.Join(toolsDir, "tsh.zip") + err = archive.CompressDirToZipFile(ctx, sourceDir, archivePath) + require.NoError(t, err) + require.FileExists(t, archivePath, "archive not created") + + err = replaceZip(toolsDir, archivePath, extractDir, []string{"tsh", "tctl"}) + require.NoError(t, err) + assert.FileExists(t, filepath.Join(toolsDir, "tsh"), "script not created") + assert.FileExists(t, filepath.Join(toolsDir, "tctl"), "script not created") + + data, err := os.ReadFile(filepath.Join(toolsDir, "tsh")) + require.NoError(t, err) + assert.Equal(t, script, string(data)) + }) +} + +// TestRemoveWithSuffix verifies that helper for the cleanup removes directories +func TestRemoveWithSuffix(t *testing.T) { + testDir := t.TempDir() + dirForRemove := "test-extract-pkg" + + // Creates directories `test/test-extract-pkg/test-extract-pkg` with exact names + // to ensure that only root one going to be removed recursively without any error. + path := filepath.Join(testDir, dirForRemove, dirForRemove) + require.NoError(t, os.MkdirAll(path, 0o755)) + // Also we create the directory that needs to be skipped, and it matches the remove + // pattern `test/skip-test-extract-pkg/test-extract-pkg`. + skipName := "skip-" + dirForRemove + skipPath := filepath.Join(testDir, skipName) + dirInSkipPath := filepath.Join(skipPath, dirForRemove) + require.NoError(t, os.MkdirAll(skipPath, 0o755)) + + err := RemoveWithSuffix(testDir, dirForRemove, []string{skipName}) + require.NoError(t, err) + + _, err = os.Stat(filepath.Join(testDir, dirForRemove)) + assert.True(t, os.IsNotExist(err)) + + filePath, err := os.Stat(skipPath) + require.NoError(t, err) + assert.True(t, filePath.IsDir()) + + _, err = os.Stat(dirInSkipPath) + assert.True(t, os.IsNotExist(err)) +} diff --git a/lib/utils/packaging/unarchive_unix.go b/lib/utils/packaging/unarchive_unix.go new file mode 100644 index 0000000000000..8daf1b3aa5525 --- /dev/null +++ b/lib/utils/packaging/unarchive_unix.go @@ -0,0 +1,208 @@ +//go:build !windows + +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package packaging + +import ( + "archive/tar" + "compress/gzip" + "errors" + "io" + "os" + "os/exec" + "path/filepath" + "runtime" + "slices" + + "github.com/google/renameio/v2" + "github.com/gravitational/trace" + + "github.com/gravitational/teleport/api/constants" +) + +// ReplaceToolsBinaries extracts executables specified by execNames from archivePath into +// extractDir. After each executable is extracted, it is symlinked from extractDir/[name] to +// toolsDir/[name]. +// +// For Darwin, archivePath must be a .pkg file. +// For other POSIX, archivePath must be a gzipped tarball. +func ReplaceToolsBinaries(toolsDir string, archivePath string, extractDir string, execNames []string) error { + switch runtime.GOOS { + case constants.DarwinOS: + return replacePkg(toolsDir, archivePath, extractDir, execNames) + default: + return replaceTarGz(toolsDir, archivePath, extractDir, execNames) + } +} + +// replaceTarGz un-archives the Teleport package in .tar.gz format, iterates through +// the compressed content, and ignores everything not matching the app binaries specified +// in the apps argument. The data is extracted to extractDir, and symlinks are created +// in toolsDir pointing to the extractDir path with binaries. +func replaceTarGz(toolsDir string, archivePath string, extractDir string, execNames []string) error { + if err := validateFreeSpaceTarGz(archivePath, extractDir, execNames); err != nil { + return trace.Wrap(err) + } + f, err := os.Open(archivePath) + if err != nil { + return trace.Wrap(err) + } + defer f.Close() + + gzipReader, err := gzip.NewReader(f) + if err != nil { + return trace.Wrap(err) + } + tarReader := tar.NewReader(gzipReader) + for { + header, err := tarReader.Next() + if errors.Is(err, io.EOF) { + break + } + if err != nil { + return trace.Wrap(err) + } + baseName := filepath.Base(header.Name) + // Skip over any files in the archive that are not in execNames. + if !slices.Contains(execNames, baseName) { + continue + } + + if err = func(header *tar.Header) error { + tempFile, err := renameio.TempFile(extractDir, filepath.Join(toolsDir, baseName)) + if err != nil { + return trace.Wrap(err) + } + defer tempFile.Cleanup() + if err := os.Chmod(tempFile.Name(), 0o755); err != nil { + return trace.Wrap(err) + } + if _, err := io.Copy(tempFile, tarReader); err != nil { + return trace.Wrap(err) + } + if err := tempFile.CloseAtomicallyReplace(); err != nil { + return trace.Wrap(err) + } + return trace.Wrap(tempFile.Cleanup()) + }(header); err != nil { + return trace.Wrap(err) + } + } + + return trace.Wrap(gzipReader.Close()) +} + +// validateFreeSpaceTarGz validates that extraction size match available disk space in `extractDir`. +func validateFreeSpaceTarGz(archivePath string, extractDir string, execNames []string) error { + f, err := os.Open(archivePath) + if err != nil { + return trace.Wrap(err) + } + defer f.Close() + + var totalSize uint64 + gzipReader, err := gzip.NewReader(f) + if err != nil { + return trace.Wrap(err) + } + tarReader := tar.NewReader(gzipReader) + for { + header, err := tarReader.Next() + if errors.Is(err, io.EOF) { + break + } + if err != nil { + return trace.Wrap(err) + } + baseName := filepath.Base(header.Name) + // Skip over any files in the archive that are not defined execNames. + if !slices.Contains(execNames, baseName) { + continue + } + totalSize += uint64(header.Size) + } + + return trace.Wrap(checkFreeSpace(extractDir, totalSize)) +} + +// replacePkg expands the Teleport package in .pkg format using the platform-dependent pkgutil utility. +// The data is extracted to extractDir, and symlinks are created in toolsDir pointing to the binaries +// in extractDir. Before creating the symlinks, each binary must be executed at least once to pass +// OS signature verification. +func replacePkg(toolsDir string, archivePath string, extractDir string, execNames []string) error { + // Use "pkgutil" from the filesystem to expand the archive. In theory .pkg + // files are xz archives, however it's still safer to use "pkgutil" in-case + // Apple makes non-standard changes to the format. + // + // Full command: pkgutil --expand-full NAME.pkg DIRECTORY/ + pkgutil, err := exec.LookPath("pkgutil") + if err != nil { + return trace.Wrap(err) + } + + if err = exec.Command(pkgutil, "--expand-full", archivePath, extractDir).Run(); err != nil { + return trace.Wrap(err) + } + + err = filepath.Walk(extractDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return trace.Wrap(err) + } + if info.IsDir() { + return nil + } + // Skip over any files in the archive that are not in execNames. + if !slices.ContainsFunc(execNames, func(s string) bool { + return filepath.Base(info.Name()) == s + }) { + return nil + } + + // The first time a signed and notarized binary macOS application is run, + // execution is paused while it gets sent to Apple to verify. Once Apple + // approves the binary, the "com.apple.macl" extended attribute is added + // and the process is allowed to execute. This process is not concurrent, any + // other operations (like moving the application) on the application during + // this time will lead to the application being sent SIGKILL. + // + // Since apps have to be concurrent, execute app before performing any + // swap operations. This ensures that the "com.apple.macl" extended + // attribute is set and macOS will not send a SIGKILL to the process + // if multiple processes are trying to operate on it. + command := exec.Command(path, "version") + command.Env = []string{"TELEPORT_TOOLS_VERSION=off"} + if err := command.Run(); err != nil { + return trace.WrapWithMessage(err, "failed to validate binary") + } + + // Due to macOS applications not being a single binary (they are a + // directory), atomic operations are not possible. To work around this, use + // a symlink (which can be atomically swapped), then do a cleanup pass + // removing any stale copies of the expanded package. + newName := filepath.Join(toolsDir, filepath.Base(path)) + if err := renameio.Symlink(path, newName); err != nil { + return trace.Wrap(err) + } + + return nil + }) + + return trace.Wrap(err) +} diff --git a/lib/utils/packaging/unarchive_windows.go b/lib/utils/packaging/unarchive_windows.go new file mode 100644 index 0000000000000..c07471adce83c --- /dev/null +++ b/lib/utils/packaging/unarchive_windows.go @@ -0,0 +1,30 @@ +//go:build windows + +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package packaging + +// ReplaceToolsBinaries extracts executables specified by execNames from archivePath into +// extractDir. After each executable is extracted, it is symlinked from extractDir/[name] to +// toolsDir/[name]. +// +// For Windows, archivePath must be a .zip file. +func ReplaceToolsBinaries(toolsDir string, archivePath string, extractPath string, execNames []string) error { + return replaceZip(toolsDir, archivePath, extractPath, execNames) +} diff --git a/lib/utils/signal/stack_handler.go b/lib/utils/signal/stack_handler.go new file mode 100644 index 0000000000000..0fcbefb081d11 --- /dev/null +++ b/lib/utils/signal/stack_handler.go @@ -0,0 +1,98 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package signal + +import ( + "container/list" + "context" + "os" + "os/signal" + "sync" + "syscall" +) + +// Handler implements stack for context cancellation. +type Handler struct { + mu sync.Mutex + list *list.List +} + +var handler = &Handler{ + list: list.New(), +} + +// GetSignalHandler returns global singleton instance of signal +func GetSignalHandler() *Handler { + return handler +} + +// NotifyContext creates context which going to be canceled after SIGINT, SIGTERM +// in order of adding them to the stack. When very first context is canceled +// we stop watching the OS signals. +func (s *Handler) NotifyContext(parent context.Context) (context.Context, context.CancelFunc) { + s.mu.Lock() + defer s.mu.Unlock() + + if s.list.Len() == 0 { + s.listenSignals() + } + + ctx, cancel := context.WithCancel(parent) + element := s.list.PushBack(cancel) + + return ctx, func() { + s.mu.Lock() + defer s.mu.Unlock() + + s.list.Remove(element) + cancel() + } +} + +// listenSignals sets up the signal listener for SIGINT, SIGTERM. +func (s *Handler) listenSignals() { + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) + go func() { + for { + if sig := <-sigChan; sig == nil { + return + } + if !s.cancelNext() { + signal.Stop(sigChan) + return + } + } + }() +} + +// cancelNext calls the most recent cancel func in the stack. +func (s *Handler) cancelNext() bool { + s.mu.Lock() + defer s.mu.Unlock() + + if s.list.Len() > 0 { + cancel := s.list.Remove(s.list.Back()) + if cancel != nil { + cancel.(context.CancelFunc)() + } + } + + return s.list.Len() != 0 +} diff --git a/lib/utils/signal/stack_handler_test.go b/lib/utils/signal/stack_handler_test.go new file mode 100644 index 0000000000000..b900939b886e8 --- /dev/null +++ b/lib/utils/signal/stack_handler_test.go @@ -0,0 +1,88 @@ +/* + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package signal + +import ( + "context" + "os" + "syscall" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestGetSignalHandler verifies the cancellation stack order. +func TestGetSignalHandler(t *testing.T) { + testHandler := GetSignalHandler() + parent := context.Background() + + ctx1, cancel1 := testHandler.NotifyContext(parent) + ctx2, cancel2 := testHandler.NotifyContext(parent) + ctx3, cancel3 := testHandler.NotifyContext(parent) + ctx4, cancel4 := testHandler.NotifyContext(parent) + t.Cleanup(func() { + cancel4() + cancel2() + cancel1() + }) + + // Verify that all context not canceled. + require.NoError(t, ctx4.Err()) + require.NoError(t, ctx3.Err()) + require.NoError(t, ctx2.Err()) + require.NoError(t, ctx1.Err()) + + // Cancel context manually to ensure it was removed from stack in right order. + cancel3() + + // Check that last added context is canceled. + require.NoError(t, syscall.Kill(os.Getpid(), syscall.SIGINT)) + select { + case <-ctx4.Done(): + assert.ErrorIs(t, ctx3.Err(), context.Canceled) + assert.NoError(t, ctx2.Err()) + assert.NoError(t, ctx1.Err()) + case <-time.After(time.Second): + assert.Fail(t, "context 3 must be canceled") + } + + // Send interrupt signal to cancel next context in the stack. + require.NoError(t, syscall.Kill(os.Getpid(), syscall.SIGINT)) + select { + case <-ctx2.Done(): + assert.ErrorIs(t, ctx4.Err(), context.Canceled) + assert.ErrorIs(t, ctx3.Err(), context.Canceled) + assert.NoError(t, ctx1.Err()) + case <-time.After(time.Second): + assert.Fail(t, "context 2 must be canceled") + } + + // All context must be canceled. + require.NoError(t, syscall.Kill(os.Getpid(), syscall.SIGINT)) + select { + case <-ctx1.Done(): + assert.ErrorIs(t, ctx4.Err(), context.Canceled) + assert.ErrorIs(t, ctx3.Err(), context.Canceled) + assert.ErrorIs(t, ctx2.Err(), context.Canceled) + case <-time.After(time.Second): + assert.Fail(t, "context 1 must be canceled") + } +} diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go index d9faba3cfca37..dbd29a46f0c15 100644 --- a/lib/web/apiserver.go +++ b/lib/web/apiserver.go @@ -55,13 +55,16 @@ import ( "google.golang.org/protobuf/encoding/protojson" "github.com/gravitational/teleport" + "github.com/gravitational/teleport/api" apiclient "github.com/gravitational/teleport/api/client" "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/client/webclient" "github.com/gravitational/teleport/api/constants" apidefaults "github.com/gravitational/teleport/api/defaults" + autoupdatepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1" apitracing "github.com/gravitational/teleport/api/observability/tracing" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/types/autoupdate" apievents "github.com/gravitational/teleport/api/types/events" "github.com/gravitational/teleport/api/types/installers" "github.com/gravitational/teleport/api/utils/keys" @@ -112,6 +115,10 @@ const ( // assistantLimiterCapacity is the total capacity of the token bucket for the assistant rate limiter. // The bucket starts full, prefilled for a week. assistantLimiterCapacity = assistantTokensPerHour * 24 * 7 + // findEndpointCacheTTL is the cache TTL for the find endpoint generic answer. + // This cache is here to protect against accidental or intentional DDoS, the TTL must be low to quickly reflect + // cluster configuration changes. + findEndpointCacheTTL = 10 * time.Second ) // healthCheckAppServerFunc defines a function used to perform a health check @@ -159,6 +166,11 @@ type Handler struct { // an authenticated websocket so unauthenticated sockets dont get left // open. wsIODeadline time.Duration + + // findEndpointCache is used to cache the find endpoint answer. As this endpoint is unprotected and has high + // rate-limits, each call must cause minimal work. The cached answer can be modulated after, for example if the + // caller specified its Automatic Updates UUID or group. + findEndpointCache *utils.FnCache } // HandlerOption is a functional argument - an option that can be passed @@ -387,6 +399,18 @@ func NewHandler(cfg Config, opts ...HandlerOption) (*APIHandler, error) { } } + // We create the cache after applying the options to make sure we use the fake clock if it was passed. + findCache, err := utils.NewFnCache(utils.FnCacheConfig{ + TTL: findEndpointCacheTTL, + Clock: h.clock, + Context: cfg.Context, + ReloadOnErr: false, + }) + if err != nil { + return nil, trace.Wrap(err, "creating /find cache") + } + h.findEndpointCache = findCache + sessionLingeringThreshold := cachedSessionLingeringThreshold if cfg.CachedSessionLingeringThreshold != nil { sessionLingeringThreshold = *cfg.CachedSessionLingeringThreshold @@ -1406,21 +1430,50 @@ func (h *Handler) ping(w http.ResponseWriter, r *http.Request, p httprouter.Para MinClientVersion: teleport.MinClientVersion, ClusterName: h.auth.clusterName, AutomaticUpgrades: pr.ServerFeatures.GetAutomaticUpgrades(), + AutoUpdate: h.automaticUpdateSettings(r.Context()), }, nil } func (h *Handler) find(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) { - // TODO(jent,espadolini): add a time-based cache to further reduce load on this endpoint - proxyConfig, err := h.cfg.ProxySettings.GetProxySettings(r.Context()) + // cache the generic answer to avoid doing work for each request + resp, err := utils.FnCacheGet[*webclient.PingResponse](r.Context(), h.findEndpointCache, "find", func(ctx context.Context) (*webclient.PingResponse, error) { + proxyConfig, err := h.cfg.ProxySettings.GetProxySettings(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + return &webclient.PingResponse{ + Proxy: *proxyConfig, + ServerVersion: teleport.Version, + MinClientVersion: teleport.MinClientVersion, + ClusterName: h.auth.clusterName, + AutoUpdate: h.automaticUpdateSettings(ctx), + }, nil + }) if err != nil { return nil, trace.Wrap(err) } - return webclient.PingResponse{ - Proxy: *proxyConfig, - ServerVersion: teleport.Version, - MinClientVersion: teleport.MinClientVersion, - ClusterName: h.auth.clusterName, - }, nil + return resp, nil +} + +// TODO: add the request as a parameter when we'll need to modulate the content based on the UUID and group +func (h *Handler) automaticUpdateSettings(ctx context.Context) webclient.AutoUpdateSettings { + autoUpdateConfig, err := h.cfg.AccessPoint.GetAutoUpdateConfig(ctx) + // TODO(vapopov) DELETE IN v18.0.0 check of IsNotImplemented, must be backported to all latest supported versions. + if err != nil && !trace.IsNotFound(err) && !trace.IsNotImplemented(err) { + h.log.WithError(err).Warn("failed to receive AutoUpdateConfig") + } + + autoUpdateVersion, err := h.cfg.AccessPoint.GetAutoUpdateVersion(ctx) + // TODO(vapopov) DELETE IN v18.0.0 check of IsNotImplemented, must be backported to all latest supported versions. + if err != nil && !trace.IsNotFound(err) && !trace.IsNotImplemented(err) { + h.log.WithError(err).Warn("failed to receive AutoUpdateVersion") + } + + return webclient.AutoUpdateSettings{ + ToolsAutoUpdate: getToolsAutoUpdate(autoUpdateConfig), + ToolsVersion: getToolsVersion(autoUpdateVersion), + } } func (h *Handler) pingWithConnector(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) { @@ -4557,3 +4610,23 @@ func serveRobotsTxt(w http.ResponseWriter, r *http.Request, p httprouter.Params) w.Write([]byte(robots)) return nil, nil } + +func getToolsAutoUpdate(config *autoupdatepb.AutoUpdateConfig) bool { + // If we can't get the AU config or if AUs are not configured, we default to "disabled". + // This ensures we fail open and don't accidentally update agents if something is going wrong. + // If we want to enable AUs by default, it would be better to create a default "autoupdate_config" resource + // than changing this logic. + if config.GetSpec().GetTools() != nil { + return config.GetSpec().GetTools().GetMode() == autoupdate.ToolsUpdateModeEnabled + } + return false +} + +func getToolsVersion(version *autoupdatepb.AutoUpdateVersion) string { + // If we can't get the AU version or tools AU version is not specified, we default to the current proxy version. + // This ensures we always advertise a version compatible with the cluster. + if version.GetSpec().GetTools() == nil { + return api.Version + } + return version.GetSpec().GetTools().GetTargetVersion() +} diff --git a/lib/web/apiserver_ping_test.go b/lib/web/apiserver_ping_test.go index 330aebcacd954..02294751947c5 100644 --- a/lib/web/apiserver_ping_test.go +++ b/lib/web/apiserver_ping_test.go @@ -22,12 +22,16 @@ import ( "testing" "github.com/gravitational/roundtrip" + "github.com/gravitational/trace" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/gravitational/teleport/api" "github.com/gravitational/teleport/api/client/webclient" "github.com/gravitational/teleport/api/constants" + autoupdatev1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/types/autoupdate" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/modules" ) @@ -231,5 +235,140 @@ func TestPing_minimalAPI(t *testing.T) { require.NoError(t, resp.Body.Close()) }) } +} + +// TestPing_autoUpdateResources tests that find endpoint return correct data related to auto updates. +func TestPing_autoUpdateResources(t *testing.T) { + env := newWebPack(t, 1, func(cfg *proxyConfig) { + cfg.minimalHandler = true + }) + proxy := env.proxies[0] + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + req, err := http.NewRequest(http.MethodGet, proxy.newClient(t).Endpoint("webapi", "find"), nil) + require.NoError(t, err) + req.Host = proxy.handler.handler.cfg.ProxyPublicAddrs[0].Host() + + tests := []struct { + name string + config *autoupdatev1pb.AutoUpdateConfigSpec + version *autoupdatev1pb.AutoUpdateVersionSpec + cleanup bool + expected webclient.AutoUpdateSettings + }{ + { + name: "resources not defined", + expected: webclient.AutoUpdateSettings{ + ToolsVersion: api.Version, + ToolsAutoUpdate: false, + }, + }, + { + name: "enable auto update", + config: &autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeEnabled, + }, + }, + expected: webclient.AutoUpdateSettings{ + ToolsAutoUpdate: true, + ToolsVersion: api.Version, + }, + cleanup: true, + }, + { + name: "empty config and version", + config: &autoupdatev1pb.AutoUpdateConfigSpec{}, + version: &autoupdatev1pb.AutoUpdateVersionSpec{}, + expected: webclient.AutoUpdateSettings{ + ToolsVersion: api.Version, + ToolsAutoUpdate: false, + }, + cleanup: true, + }, + { + name: "set tools auto update version", + version: &autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3", + }, + }, + expected: webclient.AutoUpdateSettings{ + ToolsVersion: "1.2.3", + ToolsAutoUpdate: false, + }, + cleanup: true, + }, + { + name: "enable tools auto update and set version", + config: &autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeEnabled, + }, + }, + version: &autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "1.2.3", + }, + }, + expected: webclient.AutoUpdateSettings{ + ToolsAutoUpdate: true, + ToolsVersion: "1.2.3", + }, + }, + { + name: "modify auto update config and version", + config: &autoupdatev1pb.AutoUpdateConfigSpec{ + Tools: &autoupdatev1pb.AutoUpdateConfigSpecTools{ + Mode: autoupdate.ToolsUpdateModeDisabled, + }, + }, + version: &autoupdatev1pb.AutoUpdateVersionSpec{ + Tools: &autoupdatev1pb.AutoUpdateVersionSpecTools{ + TargetVersion: "3.2.1", + }, + }, + expected: webclient.AutoUpdateSettings{ + ToolsAutoUpdate: false, + ToolsVersion: "3.2.1", + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if tc.config != nil { + config, err := autoupdate.NewAutoUpdateConfig(tc.config) + require.NoError(t, err) + _, err = env.server.Auth().UpsertAutoUpdateConfig(ctx, config) + require.NoError(t, err) + } + if tc.version != nil { + version, err := autoupdate.NewAutoUpdateVersion(tc.version) + require.NoError(t, err) + _, err = env.server.Auth().UpsertAutoUpdateVersion(ctx, version) + require.NoError(t, err) + } + + // expire the fn cache to force the next answer to be fresh + for _, proxy := range env.proxies { + proxy.clock.Advance(2 * findEndpointCacheTTL) + } + + resp, err := client.NewInsecureWebClient().Do(req) + require.NoError(t, err) + + pr := &webclient.PingResponse{} + require.NoError(t, json.NewDecoder(resp.Body).Decode(pr)) + require.NoError(t, resp.Body.Close()) + + assert.Equal(t, tc.expected, pr.AutoUpdate) + if tc.cleanup { + require.NotErrorIs(t, env.server.Auth().DeleteAutoUpdateConfig(ctx), &trace.NotFoundError{}) + require.NotErrorIs(t, env.server.Auth().DeleteAutoUpdateVersion(ctx), &trace.NotFoundError{}) + } + }) + } } diff --git a/tool/tctl/common/autoupdate_command.go b/tool/tctl/common/autoupdate_command.go new file mode 100644 index 0000000000000..76857b7d3b745 --- /dev/null +++ b/tool/tctl/common/autoupdate_command.go @@ -0,0 +1,285 @@ +/* + * Teleport + * Copyright (C) 2025 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package common + +import ( + "context" + "fmt" + "io" + "os" + + "github.com/alecthomas/kingpin/v2" + "github.com/coreos/go-semver/semver" + "github.com/gravitational/trace" + + "github.com/gravitational/teleport" + "github.com/gravitational/teleport/api/client/webclient" + autoupdatev1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/autoupdate/v1" + "github.com/gravitational/teleport/api/types/autoupdate" + "github.com/gravitational/teleport/lib/auth/authclient" + "github.com/gravitational/teleport/lib/service/servicecfg" + "github.com/gravitational/teleport/lib/utils" +) + +// maxRetries is the default number of RPC call retries to prevent parallel create/update errors. +const maxRetries = 3 + +// AutoUpdateCommand implements the `tctl autoupdate` command for managing +// autoupdate process for tools and agents. +type AutoUpdateCommand struct { + app *kingpin.Application + + targetCmd *kingpin.CmdClause + enableCmd *kingpin.CmdClause + disableCmd *kingpin.CmdClause + statusCmd *kingpin.CmdClause + + toolsTargetVersion string + proxy string + format string + + clear bool + + // stdout allows to switch standard output source for resource command. Used in tests. + stdout io.Writer + insecure bool +} + +// Initialize allows AutoUpdateCommand to plug itself into the CLI parser. +func (c *AutoUpdateCommand) Initialize(app *kingpin.Application, _ *servicecfg.Config) { + c.app = app + autoUpdateCmd := app.Command("autoupdate", "Manage auto update configuration.") + + clientToolsCmd := autoUpdateCmd.Command("client-tools", "Manage client tools auto update configuration.") + + c.statusCmd = clientToolsCmd.Command("status", "Prints if the client tools updates are enabled/disabled, and the target version in specified format.") + c.statusCmd.Flag("proxy", "Address of the Teleport proxy. When defined this address will be used to retrieve client tools auto update configuration.").StringVar(&c.proxy) + c.statusCmd.Flag("format", "Output format: 'yaml' or 'json'").Default(teleport.YAML).StringVar(&c.format) + + c.enableCmd = clientToolsCmd.Command("enable", "Enables client tools auto updates. Clients will be told to update to the target version.") + c.disableCmd = clientToolsCmd.Command("disable", "Disables client tools auto updates. Clients will not be told to update to the target version.") + + c.targetCmd = clientToolsCmd.Command("target", "Sets the client tools target version. This command is not supported on Teleport Cloud.") + c.targetCmd.Arg("version", "Client tools target version. Clients will be told to update to this version.").StringVar(&c.toolsTargetVersion) + c.targetCmd.Flag("clear", "removes the target version, Teleport will default to its current proxy version.").BoolVar(&c.clear) + + if c.stdout == nil { + c.stdout = os.Stdout + } +} + +// TryRun takes the CLI command as an argument and executes it. +func (c *AutoUpdateCommand) TryRun(ctx context.Context, cmd string, client *authclient.Client) (match bool, err error) { + switch { + case cmd == c.targetCmd.FullCommand(): + err = c.TargetVersion(ctx, client) + case cmd == c.enableCmd.FullCommand(): + err = c.SetModeCommand(true)(ctx, client) + case cmd == c.disableCmd.FullCommand(): + err = c.SetModeCommand(false)(ctx, client) + case c.proxy == "" && cmd == c.statusCmd.FullCommand(): + err = c.Status(ctx, client) + case c.proxy != "" && cmd == c.statusCmd.FullCommand(): + err = c.StatusByProxy(ctx) + return true, trace.Wrap(err) + default: + return false, nil + } + + return true, trace.Wrap(err) +} + +// TargetVersion creates or updates AutoUpdateVersion resource with client tools target version. +func (c *AutoUpdateCommand) TargetVersion(ctx context.Context, client *authclient.Client) error { + var err error + switch { + case c.clear: + err = c.clearTargetVersion(ctx, client) + case c.toolsTargetVersion != "": + // For parallel requests where we attempt to create a resource simultaneously, retries should be implemented. + // The same approach applies to updates if the resource has been deleted during the process. + // Second create request must return `AlreadyExists` error, update for deleted resource `NotFound` error. + for i := 0; i < maxRetries; i++ { + err = c.setTargetVersion(ctx, client) + if err == nil { + break + } + if !trace.IsNotFound(err) && !trace.IsAlreadyExists(err) { + return trace.Wrap(err) + } + } + } + return trace.Wrap(err) +} + +// SetModeCommand returns a command to enable or disable client tools auto-updates in the cluster. +func (c *AutoUpdateCommand) SetModeCommand(enabled bool) func(ctx context.Context, client *authclient.Client) error { + return func(ctx context.Context, client *authclient.Client) error { + // For parallel requests where we attempt to create a resource simultaneously, retries should be implemented. + // The same approach applies to updates if the resource has been deleted during the process. + // Second create request must return `AlreadyExists` error, update for deleted resource `NotFound` error. + for i := 0; i < maxRetries; i++ { + err := c.setMode(ctx, client, enabled) + if err == nil { + break + } + if !trace.IsNotFound(err) && !trace.IsAlreadyExists(err) { + return trace.Wrap(err) + } + } + return nil + } +} + +// getResponse is structure for formatting the client tools auto update response. +type getResponse struct { + Mode string `json:"mode"` + TargetVersion string `json:"target_version"` +} + +// Status makes request to auth service to fetch client tools auto update version and mode. +func (c *AutoUpdateCommand) Status(ctx context.Context, client *authclient.Client) error { + var response getResponse + config, err := client.GetAutoUpdateConfig(ctx) + if err != nil && !trace.IsNotFound(err) { + return trace.Wrap(err) + } + if config != nil && config.Spec.Tools != nil { + response.Mode = config.Spec.Tools.Mode + } + + version, err := client.GetAutoUpdateVersion(ctx) + if err != nil && !trace.IsNotFound(err) { + return trace.Wrap(err) + } + if version != nil && version.Spec.Tools != nil { + response.TargetVersion = version.Spec.Tools.TargetVersion + } + + return c.printResponse(response) +} + +// StatusByProxy makes request to `webapi/find` endpoint to fetch tools auto update version and mode +// without authentication. +func (c *AutoUpdateCommand) StatusByProxy(ctx context.Context) error { + find, err := webclient.Find(&webclient.Config{ + Context: ctx, + ProxyAddr: c.proxy, + Insecure: c.insecure, + }) + if err != nil { + return trace.Wrap(err) + } + mode := autoupdate.ToolsUpdateModeDisabled + if find.AutoUpdate.ToolsAutoUpdate { + mode = autoupdate.ToolsUpdateModeEnabled + } + return c.printResponse(getResponse{ + TargetVersion: find.AutoUpdate.ToolsVersion, + Mode: mode, + }) +} + +func (c *AutoUpdateCommand) setMode(ctx context.Context, client *authclient.Client, enabled bool) error { + setMode := client.UpdateAutoUpdateConfig + config, err := client.GetAutoUpdateConfig(ctx) + if trace.IsNotFound(err) { + if config, err = autoupdate.NewAutoUpdateConfig(&autoupdatev1pb.AutoUpdateConfigSpec{}); err != nil { + return trace.Wrap(err) + } + setMode = client.CreateAutoUpdateConfig + } else if err != nil { + return trace.Wrap(err) + } + + if config.Spec.Tools == nil { + config.Spec.Tools = &autoupdatev1pb.AutoUpdateConfigSpecTools{} + } + + config.Spec.Tools.Mode = autoupdate.ToolsUpdateModeDisabled + if enabled { + config.Spec.Tools.Mode = autoupdate.ToolsUpdateModeEnabled + } + if _, err := setMode(ctx, config); err != nil { + return trace.Wrap(err) + } + fmt.Fprintln(c.stdout, "client tools auto update mode has been changed") + + return nil +} + +func (c *AutoUpdateCommand) setTargetVersion(ctx context.Context, client *authclient.Client) error { + if _, err := semver.NewVersion(c.toolsTargetVersion); err != nil { + return trace.WrapWithMessage(err, "not semantic version") + } + setTargetVersion := client.UpdateAutoUpdateVersion + version, err := client.GetAutoUpdateVersion(ctx) + if trace.IsNotFound(err) { + if version, err = autoupdate.NewAutoUpdateVersion(&autoupdatev1pb.AutoUpdateVersionSpec{}); err != nil { + return trace.Wrap(err) + } + setTargetVersion = client.CreateAutoUpdateVersion + } else if err != nil { + return trace.Wrap(err) + } + if version.Spec.Tools == nil { + version.Spec.Tools = &autoupdatev1pb.AutoUpdateVersionSpecTools{} + } + if version.Spec.Tools.TargetVersion != c.toolsTargetVersion { + version.Spec.Tools.TargetVersion = c.toolsTargetVersion + if _, err := setTargetVersion(ctx, version); err != nil { + return trace.Wrap(err) + } + fmt.Fprintln(c.stdout, "client tools auto update target version has been set") + } + return nil +} + +func (c *AutoUpdateCommand) clearTargetVersion(ctx context.Context, client *authclient.Client) error { + version, err := client.GetAutoUpdateVersion(ctx) + if trace.IsNotFound(err) { + return nil + } else if err != nil { + return trace.Wrap(err) + } + if version.Spec.Tools != nil { + version.Spec.Tools = nil + if _, err := client.UpdateAutoUpdateVersion(ctx, version); err != nil { + return trace.Wrap(err) + } + fmt.Fprintln(c.stdout, "client tools auto update target version has been cleared") + } + return nil +} + +func (c *AutoUpdateCommand) printResponse(response getResponse) error { + switch c.format { + case teleport.JSON: + if err := utils.WriteJSON(c.stdout, response); err != nil { + return trace.Wrap(err) + } + case teleport.YAML: + if err := utils.WriteYAML(c.stdout, response); err != nil { + return trace.Wrap(err) + } + default: + return trace.BadParameter("unsupported output format %s, supported values are %s and %s", c.format, teleport.JSON, teleport.YAML) + } + return nil +} diff --git a/tool/tctl/common/autoupdate_command_test.go b/tool/tctl/common/autoupdate_command_test.go new file mode 100644 index 0000000000000..4902593756f2e --- /dev/null +++ b/tool/tctl/common/autoupdate_command_test.go @@ -0,0 +1,117 @@ +/* + * Teleport + * Copyright (C) 2025 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package common + +import ( + "bytes" + "context" + "testing" + + "github.com/gravitational/trace" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/api/breaker" + "github.com/gravitational/teleport/lib/auth/authclient" + "github.com/gravitational/teleport/lib/service/servicecfg" + "github.com/gravitational/teleport/lib/utils" + "github.com/gravitational/teleport/tool/teleport/testenv" +) + +// TestClientToolsAutoUpdateCommands verifies all commands related to client auto updates, by +// enabling/disabling auto update, setting the target version and retrieve it. +func TestClientToolsAutoUpdateCommands(t *testing.T) { + ctx := context.Background() + process := testenv.MakeTestServer(t) + server := process.GetAuthServer() + + // Check that AutoUpdateConfig and AutoUpdateVersion are not created. + _, err := server.GetAutoUpdateConfig(ctx) + require.True(t, trace.IsNotFound(err)) + _, err = server.GetAutoUpdateVersion(ctx) + require.True(t, trace.IsNotFound(err)) + + authClient := testenv.MakeDefaultAuthClient(t, process) + + // Enable client tools auto updates to check that AutoUpdateConfig resource is modified. + _, err = runAutoUpdateCommand(t, authClient, []string{"client-tools", "enable"}) + require.NoError(t, err) + + config, err := authClient.GetAutoUpdateConfig(ctx) + require.NoError(t, err) + assert.Equal(t, "enabled", config.Spec.Tools.Mode) + + // Disable client tools auto updates to check that AutoUpdateConfig resource is modified. + _, err = runAutoUpdateCommand(t, authClient, []string{"client-tools", "disable"}) + require.NoError(t, err) + + config, err = authClient.GetAutoUpdateConfig(ctx) + require.NoError(t, err) + assert.Equal(t, "disabled", config.Spec.Tools.Mode) + + // Set target version for client tools auto updates. + _, err = runAutoUpdateCommand(t, authClient, []string{"client-tools", "target", "1.2.3"}) + require.NoError(t, err) + + version, err := authClient.GetAutoUpdateVersion(ctx) + require.NoError(t, err) + assert.Equal(t, "1.2.3", version.Spec.Tools.TargetVersion) + + getBuf, err := runAutoUpdateCommand(t, authClient, []string{"client-tools", "status", "--format=json"}) + require.NoError(t, err) + response := mustDecodeJSON[getResponse](t, getBuf) + assert.Equal(t, "1.2.3", response.TargetVersion) + assert.Equal(t, "disabled", response.Mode) + + // Make same request with proxy flag to read command expecting the same + // response from `webapi/find` endpoint. + proxy, err := process.ProxyWebAddr() + require.NoError(t, err) + getProxyBuf, err := runAutoUpdateCommand(t, authClient, []string{"client-tools", "status", "--proxy=" + proxy.Addr, "--format=json"}) + require.NoError(t, err) + response = mustDecodeJSON[getResponse](t, getProxyBuf) + assert.Equal(t, "1.2.3", response.TargetVersion) + assert.Equal(t, "disabled", response.Mode) + + // Set clear flag for the target version update to check that it is going to be reset. + _, err = runAutoUpdateCommand(t, authClient, []string{"client-tools", "target", "--clear"}) + require.NoError(t, err) + version, err = authClient.GetAutoUpdateVersion(ctx) + require.NoError(t, err) + assert.Nil(t, version.Spec.Tools) +} + +func runAutoUpdateCommand(t *testing.T, client *authclient.Client, args []string) (*bytes.Buffer, error) { + var stdoutBuff bytes.Buffer + command := &AutoUpdateCommand{ + stdout: &stdoutBuff, + insecure: true, + } + + cfg := servicecfg.MakeDefaultConfig() + cfg.CircuitBreakerConfig = breaker.NoopBreakerConfig() + app := utils.InitCLIParser("tctl", GlobalHelpString) + command.Initialize(app, cfg) + + selectedCmd, err := app.Parse(append([]string{"autoupdate"}, args...)) + require.NoError(t, err) + + _, err = command.TryRun(context.Background(), selectedCmd, client) + return &stdoutBuff, err +} diff --git a/tool/tctl/common/cmds.go b/tool/tctl/common/cmds.go index 198dd1a060b9f..8fafdb881a404 100644 --- a/tool/tctl/common/cmds.go +++ b/tool/tctl/common/cmds.go @@ -50,6 +50,7 @@ func Commands() []CLICommand { &ACLCommand{}, &loginrule.Command{}, &IdPCommand{}, + &AutoUpdateCommand{}, } } diff --git a/tool/tctl/common/collection.go b/tool/tctl/common/collection.go index 0880de26016b9..f92370cfd2033 100644 --- a/tool/tctl/common/collection.go +++ b/tool/tctl/common/collection.go @@ -1398,7 +1398,7 @@ func (c *autoUpdateConfigCollection) writeText(w io.Writer, verbose bool) error t := asciitable.MakeTable([]string{"Name", "Tools AutoUpdate Enabled"}) t.AddRow([]string{ c.config.GetMetadata().GetName(), - fmt.Sprintf("%v", c.config.GetSpec().GetToolsAutoupdate()), + fmt.Sprintf("%v", c.config.GetSpec().GetTools().GetMode()), }) _, err := t.AsBuffer().WriteTo(w) return trace.Wrap(err) @@ -1416,7 +1416,7 @@ func (c *autoUpdateVersionCollection) writeText(w io.Writer, verbose bool) error t := asciitable.MakeTable([]string{"Name", "Tools AutoUpdate Version"}) t.AddRow([]string{ c.version.GetMetadata().GetName(), - fmt.Sprintf("%v", c.version.GetSpec().GetToolsVersion()), + fmt.Sprintf("%v", c.version.GetSpec().GetTools().TargetVersion), }) _, err := t.AsBuffer().WriteTo(w) return trace.Wrap(err) diff --git a/tool/tctl/common/resource_command.go b/tool/tctl/common/resource_command.go index e4c8bc2e5464a..ac7e5eeb68ffd 100644 --- a/tool/tctl/common/resource_command.go +++ b/tool/tctl/common/resource_command.go @@ -1145,6 +1145,8 @@ func (rc *ResourceCommand) Delete(ctx context.Context, client *authclient.Client types.KindSessionRecordingConfig, types.KindInstaller, types.KindUIConfig, + types.KindAutoUpdateConfig, + types.KindAutoUpdateVersion, } if !slices.Contains(singletonResources, rc.ref.Kind) && (rc.ref.Kind == "" || rc.ref.Name == "") { return trace.BadParameter("provide a full resource name to delete, for example:\n$ tctl rm cluster/east\n") @@ -1494,6 +1496,16 @@ func (rc *ResourceCommand) Delete(ctx context.Context, client *authclient.Client return trace.Wrap(err) } fmt.Printf("Server info %q has been deleted\n", rc.ref.Name) + case types.KindAutoUpdateConfig: + if err := client.DeleteAutoUpdateConfig(ctx); err != nil { + return trace.Wrap(err) + } + fmt.Printf("AutoUpdateConfig has been deleted\n") + case types.KindAutoUpdateVersion: + if err := client.DeleteAutoUpdateVersion(ctx); err != nil { + return trace.Wrap(err) + } + fmt.Printf("AutoUpdateVersion has been deleted\n") default: return trace.BadParameter("deleting resources of type %q is not supported", rc.ref.Kind) } diff --git a/tool/tctl/common/resource_command_test.go b/tool/tctl/common/resource_command_test.go index 2ef10964f1f7b..d189c03e9c934 100644 --- a/tool/tctl/common/resource_command_test.go +++ b/tool/tctl/common/resource_command_test.go @@ -1436,7 +1436,8 @@ metadata: name: autoupdate-config revision: 3a43b44a-201e-4d7f-aef1-ae2f6d9811ed spec: - tools_autoupdate: true + tools: + mode: enabled version: v1 ` _, err := runResourceCommand(t, fc, []string{"get", types.KindAutoUpdateConfig, "--format=json"}) @@ -1463,6 +1464,12 @@ version: v1 protocmp.IgnoreFields(&headerv1.Metadata{}, "id", "revision"), protocmp.Transform(), )) + + // Delete the resource + _, err = runResourceCommand(t, fc, []string{"rm", types.KindAutoUpdateConfig}) + require.NoError(t, err) + _, err = runResourceCommand(t, fc, []string{"get", types.KindAutoUpdateConfig}) + require.ErrorContains(t, err, "autoupdate_config \"autoupdate-config\" doesn't exist") } func testCreateAutoUpdateVersion(t *testing.T, fc *config.FileConfig) { @@ -1471,7 +1478,8 @@ metadata: name: autoupdate-version revision: 3a43b44a-201e-4d7f-aef1-ae2f6d9811ed spec: - tools_version: 1.2.3 + tools: + target_version: 1.2.3 version: v1 ` _, err := runResourceCommand(t, fc, []string{"get", types.KindAutoUpdateVersion, "--format=json"}) @@ -1498,4 +1506,10 @@ version: v1 protocmp.IgnoreFields(&headerv1.Metadata{}, "id", "revision"), protocmp.Transform(), )) + + // Delete the resource + _, err = runResourceCommand(t, fc, []string{"rm", types.KindAutoUpdateVersion}) + require.NoError(t, err) + _, err = runResourceCommand(t, fc, []string{"get", types.KindAutoUpdateVersion}) + require.ErrorContains(t, err, "autoupdate_version \"autoupdate-version\" doesn't exist") } diff --git a/tool/tctl/common/tctl.go b/tool/tctl/common/tctl.go index 263c3c47c7f9f..4d1592824439a 100644 --- a/tool/tctl/common/tctl.go +++ b/tool/tctl/common/tctl.go @@ -39,6 +39,7 @@ import ( "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/auth/state" "github.com/gravitational/teleport/lib/auth/storage" + "github.com/gravitational/teleport/lib/autoupdate/tools" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/client/identityfile" "github.com/gravitational/teleport/lib/client/mfa" @@ -98,7 +99,11 @@ type CLICommand interface { // "distributions" like OSS or Enterprise // // distribution: name of the Teleport distribution -func Run(commands []CLICommand) { +func Run(ctx context.Context, commands []CLICommand) { + if err := tools.CheckAndUpdateLocal(ctx, os.Args[1:]); err != nil { + utils.FatalError(err) + } + err := TryRun(commands, os.Args[1:]) if err != nil { var exitError *common.ExitCodeError diff --git a/tool/tctl/main.go b/tool/tctl/main.go index 51b917383f157..fcc8121f6a48f 100644 --- a/tool/tctl/main.go +++ b/tool/tctl/main.go @@ -15,12 +15,18 @@ package main import ( + "context" + + "github.com/gravitational/teleport/lib/utils/signal" "github.com/gravitational/teleport/tool/tctl/common" ) func main() { + ctx, cancel := signal.GetSignalHandler().NotifyContext(context.Background()) + defer cancel() + // aggregate common and oss-specific command variants commands := common.Commands() commands = append(commands, common.OSSCommands()...) - common.Run(commands) + common.Run(ctx, commands) } diff --git a/tool/teleport/testenv/test_server.go b/tool/teleport/testenv/test_server.go index 54f542eb58c8e..373c9748e7fc1 100644 --- a/tool/teleport/testenv/test_server.go +++ b/tool/teleport/testenv/test_server.go @@ -34,6 +34,7 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/crypto/ssh" + "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/breaker" apidefaults "github.com/gravitational/teleport/api/defaults" "github.com/gravitational/teleport/api/types" @@ -42,7 +43,10 @@ import ( apisshutils "github.com/gravitational/teleport/api/utils/sshutils" "github.com/gravitational/teleport/lib" "github.com/gravitational/teleport/lib/auth" + "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/auth/native" + "github.com/gravitational/teleport/lib/auth/state" + "github.com/gravitational/teleport/lib/auth/storage" "github.com/gravitational/teleport/lib/backend" "github.com/gravitational/teleport/lib/cloud/imds" "github.com/gravitational/teleport/lib/defaults" @@ -608,3 +612,32 @@ func startSSHServer(t *testing.T, caPubKeys []ssh.PublicKey, hostKey ssh.Signer) return lis.Addr().String() } + +// MakeDefaultAuthClient reimplements the bare minimum needed to create a +// default root-level auth client for a Teleport server started by +// MakeTestServer. +func MakeDefaultAuthClient(t *testing.T, process *service.TeleportProcess) *authclient.Client { + t.Helper() + + cfg := process.Config + identity, err := storage.ReadLocalIdentity( + filepath.Join(cfg.DataDir, teleport.ComponentProcess), + state.IdentityID{Role: types.RoleAdmin, HostUUID: cfg.HostUUID}, + ) + require.NoError(t, err) + + authConfig := new(authclient.Config) + authConfig.TLS, err = identity.TLSConfig(cfg.CipherSuites) + require.NoError(t, err) + + authConfig.AuthServers = cfg.AuthServerAddresses() + authConfig.Log = cfg.Log + + client, err := authclient.Connect(context.Background(), authConfig) + require.NoError(t, err) + t.Cleanup(func() { + _ = client.Close() + }) + + return client +} diff --git a/tool/tsh/common/tsh.go b/tool/tsh/common/tsh.go index b7f0ddf919d2f..da781a7a87d34 100644 --- a/tool/tsh/common/tsh.go +++ b/tool/tsh/common/tsh.go @@ -37,7 +37,6 @@ import ( "strconv" "strings" "sync" - "syscall" "time" "github.com/alecthomas/kingpin/v2" @@ -69,6 +68,7 @@ import ( "github.com/gravitational/teleport/lib/asciitable" "github.com/gravitational/teleport/lib/auth/authclient" wancli "github.com/gravitational/teleport/lib/auth/webauthncli" + "github.com/gravitational/teleport/lib/autoupdate/tools" "github.com/gravitational/teleport/lib/benchmark" benchmarkdb "github.com/gravitational/teleport/lib/benchmark/db" "github.com/gravitational/teleport/lib/client" @@ -90,6 +90,7 @@ import ( "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils/diagnostics/latency" "github.com/gravitational/teleport/lib/utils/mlock" + stacksignal "github.com/gravitational/teleport/lib/utils/signal" "github.com/gravitational/teleport/tool/common" ) @@ -564,7 +565,7 @@ func Main() { cmdLineOrig := os.Args[1:] var cmdLine []string - ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT) + ctx, cancel := stacksignal.GetSignalHandler().NotifyContext(context.Background()) defer cancel() // lets see: if the executable name is 'ssh' or 'scp' we convert @@ -660,6 +661,10 @@ func initLogger(cf *CLIConf) { // // DO NOT RUN TESTS that call Run() in parallel (unless you taken precautions). func Run(ctx context.Context, args []string, opts ...CliOption) error { + if err := tools.CheckAndUpdateLocal(ctx, args); err != nil { + return trace.Wrap(err) + } + cf := CLIConf{ Context: ctx, TracingProvider: tracing.NoopProvider(), @@ -1156,7 +1161,6 @@ func Run(ctx context.Context, args []string, opts ...CliOption) error { } var err error - cf.executablePath, err = os.Executable() if err != nil { return trace.Wrap(err) @@ -1377,7 +1381,7 @@ func Run(ctx context.Context, args []string, opts ...CliOption) error { case sessionsList.FullCommand(): err = onListSessions(&cf) case login.FullCommand(): - err = onLogin(&cf) + err = onLogin(&cf, args...) case logout.FullCommand(): err = onLogout(&cf) case show.FullCommand(): @@ -1827,7 +1831,7 @@ func exportFile(ctx context.Context, path string, format string) error { } // onLogin logs in with remote proxy and gets signed certificates -func onLogin(cf *CLIConf) error { +func onLogin(cf *CLIConf, reExecArgs ...string) error { autoRequest := true // special case: --request-roles=no disables auto-request behavior. if cf.DesiredRoles == "no" { @@ -1865,6 +1869,14 @@ func onLogin(cf *CLIConf) error { } tc.HomePath = cf.HomePath + // The user is not logged in and has typed in `tsh --proxy=... login`, if + // the running binary needs to be updated, update and re-exec. + if profile == nil { + if err := tools.CheckAndUpdateRemote(cf.Context, tc.WebProxyAddr, tc.InsecureSkipVerify, reExecArgs); err != nil { + return trace.Wrap(err) + } + } + // client is already logged in and profile is not expired if profile != nil && !profile.IsExpired(clockwork.NewRealClock()) { switch { @@ -1875,6 +1887,13 @@ func onLogin(cf *CLIConf) error { // current status case cf.Proxy == "" && cf.SiteName == "" && cf.DesiredRoles == "" && cf.RequestID == "" && cf.IdentityFileOut == "" || host(cf.Proxy) == host(profile.ProxyURL.Host) && cf.SiteName == profile.Cluster && cf.DesiredRoles == "" && cf.RequestID == "": + + // The user has typed `tsh login`, if the running binary needs to + // be updated, update and re-exec. + if err := tools.CheckAndUpdateRemote(cf.Context, tc.WebProxyAddr, tc.InsecureSkipVerify, reExecArgs); err != nil { + return trace.Wrap(err) + } + _, err := tc.PingAndShowMOTD(cf.Context) if err != nil { return trace.Wrap(err) @@ -1888,6 +1907,13 @@ func onLogin(cf *CLIConf) error { // if the proxy names match but nothing else is specified; show motd and update active profile and kube configs case host(cf.Proxy) == host(profile.ProxyURL.Host) && cf.SiteName == "" && cf.DesiredRoles == "" && cf.RequestID == "" && cf.IdentityFileOut == "": + + // The user has typed `tsh login`, if the running binary needs to + // be updated, update and re-exec. + if err := tools.CheckAndUpdateRemote(cf.Context, tc.WebProxyAddr, tc.InsecureSkipVerify, reExecArgs); err != nil { + return trace.Wrap(err) + } + _, err := tc.PingAndShowMOTD(cf.Context) if err != nil { return trace.Wrap(err) @@ -1958,7 +1984,11 @@ func onLogin(cf *CLIConf) error { // otherwise just pass through to standard login default: - + // The user is logged in and has typed in `tsh --proxy=... login`, if + // the running binary needs to be updated, update and re-exec. + if err := tools.CheckAndUpdateRemote(cf.Context, tc.WebProxyAddr, tc.InsecureSkipVerify, reExecArgs); err != nil { + return trace.Wrap(err) + } } } diff --git a/web/packages/teleterm/src/mainProcess/mainProcess.ts b/web/packages/teleterm/src/mainProcess/mainProcess.ts index 4edb0ff81afe3..74218e71b90d9 100644 --- a/web/packages/teleterm/src/mainProcess/mainProcess.ts +++ b/web/packages/teleterm/src/mainProcess/mainProcess.ts @@ -45,6 +45,10 @@ import { import { getAssetPath } from 'teleterm/mainProcess/runtimeSettings'; import { RootClusterUri } from 'teleterm/ui/uri'; import Logger from 'teleterm/logger'; +import { + TSH_AUTOUPDATE_ENV_VAR, + TSH_AUTOUPDATE_OFF, +} from 'teleterm/node/tshAutoupdate'; import { ConfigService, @@ -164,6 +168,7 @@ export default class MainProcess { env: { ...process.env, TELEPORT_HOME: homeDir, + [TSH_AUTOUPDATE_ENV_VAR]: TSH_AUTOUPDATE_OFF, }, }); diff --git a/web/packages/teleterm/src/node/README.md b/web/packages/teleterm/src/node/README.md new file mode 100644 index 0000000000000..28ea74a4d8d5c --- /dev/null +++ b/web/packages/teleterm/src/node/README.md @@ -0,0 +1,2 @@ +Files in this directory are executed within a Node.js process, be it the main process or the shared +process. diff --git a/web/packages/teleterm/src/node/tshAutoupdate.ts b/web/packages/teleterm/src/node/tshAutoupdate.ts new file mode 100644 index 0000000000000..8ac6d73d9b8b2 --- /dev/null +++ b/web/packages/teleterm/src/node/tshAutoupdate.ts @@ -0,0 +1,27 @@ +/** + * Teleport + * Copyright (C) 2024 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * An env var which controls whether tsh is going to download an up-to-date version of itself + * to ~/.tsh/bin and re-execute itself. In Connect, we always want it to be set to 'off', as Connect + * needs to use the bundled tsh where the version of tsh matches exactly the version of Connect. + * + * See RFD 144 for more details. + */ +export const TSH_AUTOUPDATE_ENV_VAR = 'TELEPORT_TOOLS_VERSION'; +export const TSH_AUTOUPDATE_OFF = 'off'; diff --git a/web/packages/teleterm/src/services/pty/ptyHost/buildPtyOptions.ts b/web/packages/teleterm/src/services/pty/ptyHost/buildPtyOptions.ts index 9a90878dae7f4..3953e5e8378d1 100644 --- a/web/packages/teleterm/src/services/pty/ptyHost/buildPtyOptions.ts +++ b/web/packages/teleterm/src/services/pty/ptyHost/buildPtyOptions.ts @@ -19,6 +19,10 @@ import path, { delimiter } from 'path'; import { RuntimeSettings } from 'teleterm/mainProcess/types'; import { PtyProcessOptions } from 'teleterm/sharedProcess/ptyHost'; import { assertUnreachable } from 'teleterm/ui/utils'; +import { + TSH_AUTOUPDATE_ENV_VAR, + TSH_AUTOUPDATE_OFF, +} from 'teleterm/node/tshAutoupdate'; import { PtyCommand, @@ -53,6 +57,9 @@ export async function buildPtyOptions( throw error; }) .then(({ shellEnv, creationStatus }) => { + // combinedEnv is going to be used as env by every command coming out of buildPtyOptions. Some + // commands might add extra variables, but they shouldn't remove any of the env vars that are + // added here. const combinedEnv = { ...process.env, ...shellEnv, @@ -61,6 +68,7 @@ export async function buildPtyOptions( TELEPORT_HOME: settings.tshd.homeDir, TELEPORT_CLUSTER: cmd.clusterName, TELEPORT_PROXY: cmd.proxyHost, + [TSH_AUTOUPDATE_ENV_VAR]: TSH_AUTOUPDATE_OFF, }; return {