From f19a9c49af3f1471bca6bec8139f8ac9c6a3be76 Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Tue, 23 May 2023 14:53:48 +0200 Subject: [PATCH] wip --- lib/config/deviceconfiguration.pb.go | 162 +++++--- lib/connections/service.go | 40 +- lib/model/fakeconns_test.go | 6 +- lib/model/folder_recvonly_test.go | 14 +- lib/model/folder_sendrecv_test.go | 2 +- lib/model/mocks/model.go | 72 ++-- lib/model/model.go | 101 +++-- lib/model/model_test.go | 137 +++---- lib/model/requests_test.go | 22 +- lib/model/testutils_test.go | 11 + lib/protocol/benchmark_test.go | 12 +- lib/protocol/bep.pb.go | 435 +++++++++++---------- lib/protocol/common_test.go | 20 +- lib/protocol/encryption.go | 30 +- lib/protocol/nativemodel_darwin.go | 16 +- lib/protocol/protocol.go | 88 ++++- lib/protocol/protocol_test.go | 4 +- proto/lib/config/deviceconfiguration.proto | 1 + proto/lib/protocol/bep.proto | 3 +- test/h1/config.xml | 77 +--- test/h2/config.xml | 101 +---- 21 files changed, 690 insertions(+), 664 deletions(-) diff --git a/lib/config/deviceconfiguration.pb.go b/lib/config/deviceconfiguration.pb.go index e07bb1eb0..0292c1e8d 100644 --- a/lib/config/deviceconfiguration.pb.go +++ b/lib/config/deviceconfiguration.pb.go @@ -44,6 +44,7 @@ type DeviceConfiguration struct { MaxRequestKiB int `protobuf:"varint,16,opt,name=max_request_kib,json=maxRequestKib,proto3,casttype=int" json:"maxRequestKiB" xml:"maxRequestKiB"` Untrusted bool `protobuf:"varint,17,opt,name=untrusted,proto3" json:"untrusted" xml:"untrusted"` RemoteGUIPort int `protobuf:"varint,18,opt,name=remote_gui_port,json=remoteGuiPort,proto3,casttype=int" json:"remoteGUIPort" xml:"remoteGUIPort"` + MultipleConnections int `protobuf:"varint,19,opt,name=multiple_connections,json=multipleConnections,proto3,casttype=int" json:"multipleConnections" xml:"multipleConnections"` } func (m *DeviceConfiguration) Reset() { *m = DeviceConfiguration{} } @@ -88,72 +89,74 @@ func init() { } var fileDescriptor_744b782bd13071dd = []byte{ - // 1026 bytes of a gzipped FileDescriptorProto + // 1064 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x55, 0xbf, 0x6f, 0xdb, 0x46, - 0x18, 0x15, 0xeb, 0xc4, 0xb6, 0xce, 0x3f, 0x64, 0xd3, 0x88, 0xc3, 0x18, 0x88, 0x4e, 0x50, 0x35, - 0x28, 0x68, 0x22, 0x17, 0x6e, 0x27, 0xa3, 0x2d, 0x50, 0xc6, 0x68, 0x63, 0x18, 0x4d, 0x5c, 0x16, - 0x5d, 0xbc, 0xb0, 0x24, 0xef, 0xac, 0x1c, 0x2c, 0xf2, 0x58, 0xf2, 0xa8, 0x58, 0x40, 0xff, 0x80, - 0x76, 0x2b, 0x02, 0x74, 0xea, 0x92, 0xf6, 0xdf, 0xe8, 0xd0, 0xd5, 0x9b, 0x35, 0x16, 0x1d, 0x0e, - 0x88, 0xbd, 0x71, 0x29, 0xc0, 0x31, 0x53, 0x71, 0x77, 0x14, 0x45, 0xca, 0x51, 0x50, 0xa0, 0x1b, - 0xef, 0xbd, 0x77, 0xef, 0xdd, 0xf7, 0xe9, 0xbb, 0x13, 0xe8, 0x0c, 0x88, 0xbb, 0xeb, 0xd1, 0xe0, - 0x94, 0xf4, 0x77, 0x11, 0x1e, 0x12, 0x0f, 0xab, 0x45, 0x12, 0x39, 0x8c, 0xd0, 0xa0, 0x17, 0x46, - 0x94, 0x51, 0x7d, 0x51, 0x81, 0x3b, 0xdb, 0x42, 0x2d, 0x21, 0x8f, 0x0e, 0x76, 0x5d, 0x1c, 0x2a, - 0x7e, 0xe7, 0x5e, 0xc9, 0x85, 0xba, 0x31, 0x8e, 0x86, 0x18, 0xe5, 0x54, 0x1d, 0x9f, 0x33, 0xf5, - 0xd9, 0xfe, 0x67, 0x03, 0x6c, 0x1d, 0xc8, 0x8c, 0xc7, 0xe5, 0x0c, 0xfd, 0x4f, 0x0d, 0xd4, 0x55, - 0xb6, 0x4d, 0x90, 0xa1, 0xb5, 0xb4, 0xee, 0xaa, 0xf9, 0x9b, 0x76, 0xc1, 0x61, 0xed, 0x6f, 0x0e, - 0x3f, 0xee, 0x13, 0xf6, 0x3c, 0x71, 0x7b, 0x1e, 0xf5, 0x77, 0xe3, 0x51, 0xe0, 0xb1, 0xe7, 0x24, - 0xe8, 0x97, 0xbe, 0xca, 0x27, 0xea, 0x29, 0xf7, 0xc3, 0x83, 0x2b, 0x0e, 0x97, 0x27, 0xdf, 0x29, - 0x87, 0xcb, 0x28, 0xff, 0xce, 0x38, 0x6c, 0x9e, 0xfb, 0x83, 0xfd, 0x36, 0x41, 0x0f, 0x1d, 0xc6, - 0xa2, 0x76, 0x2b, 0xa0, 0x08, 0x9f, 0x3a, 0xc9, 0x80, 0xed, 0xb7, 0x59, 0x94, 0xe0, 0x76, 0x7a, - 0xd9, 0x59, 0xca, 0xc9, 0xec, 0xb2, 0x53, 0x6c, 0xfc, 0x71, 0xdc, 0xd1, 0x5e, 0x8e, 0x3b, 0x85, - 0xe9, 0xab, 0x71, 0x47, 0xb3, 0x26, 0x2c, 0xd2, 0x8f, 0xc1, 0xad, 0xc0, 0xf1, 0xb1, 0xf1, 0x5e, - 0x4b, 0xeb, 0xd6, 0xcd, 0x4f, 0x52, 0x0e, 0xe5, 0x3a, 0xe3, 0xf0, 0x9e, 0x8c, 0x13, 0x0b, 0xe9, - 0xf9, 0x90, 0xfa, 0x84, 0x61, 0x3f, 0x64, 0x23, 0x91, 0xb4, 0xf5, 0x16, 0xdc, 0x92, 0x3b, 0xf5, - 0x73, 0x50, 0x77, 0x10, 0x8a, 0x70, 0x1c, 0xe3, 0xd8, 0x58, 0x68, 0x2d, 0x74, 0xeb, 0xe6, 0x49, - 0xca, 0xe1, 0x14, 0xcc, 0x38, 0x7c, 0x20, 0xbd, 0x73, 0xa4, 0xe4, 0xdc, 0x2a, 0x4a, 0x42, 0xa3, - 0xc0, 0xf1, 0x89, 0x27, 0xb2, 0x36, 0x6f, 0xe8, 0xde, 0x5c, 0x76, 0x96, 0x72, 0x81, 0x35, 0xf5, - 0xd5, 0x87, 0x60, 0xc5, 0xa3, 0x7e, 0x28, 0x56, 0x84, 0x06, 0xc6, 0xad, 0x96, 0xd6, 0x5d, 0xdf, - 0xbb, 0xd3, 0x2b, 0x7a, 0xfc, 0x78, 0x4a, 0x9a, 0x9f, 0xa6, 0x1c, 0x96, 0xd5, 0x19, 0x87, 0xdb, - 0xf2, 0x50, 0x25, 0x4c, 0x35, 0x3a, 0xbd, 0xec, 0x6c, 0xcc, 0x82, 0x56, 0x79, 0xab, 0x8e, 0x41, - 0xdd, 0xc3, 0x11, 0xb3, 0x65, 0x23, 0x6f, 0xcb, 0x46, 0x3e, 0x11, 0xbf, 0x9d, 0x00, 0x9f, 0xaa, - 0x66, 0xde, 0x57, 0xde, 0x39, 0xf0, 0x96, 0x86, 0xde, 0x9d, 0xc3, 0x59, 0x85, 0x8b, 0x7e, 0x02, - 0x00, 0x09, 0x58, 0x44, 0x51, 0xe2, 0xe1, 0xc8, 0x58, 0x6c, 0x69, 0xdd, 0x65, 0x73, 0x3f, 0xe5, - 0xb0, 0x84, 0x66, 0x1c, 0xde, 0x51, 0x53, 0x52, 0x40, 0x45, 0x11, 0x8d, 0x19, 0xcc, 0x2a, 0xed, - 0xd3, 0x7f, 0xd7, 0xc0, 0x4e, 0x7c, 0x46, 0x42, 0x7b, 0x82, 0x89, 0xf1, 0xb6, 0x23, 0xec, 0xd3, - 0xa1, 0x33, 0x88, 0x8d, 0x25, 0x19, 0x86, 0x52, 0x0e, 0x0d, 0xa1, 0x3a, 0x2c, 0x89, 0xac, 0x5c, - 0x93, 0x71, 0xf8, 0xbe, 0x8c, 0x9e, 0x27, 0x28, 0x0e, 0x72, 0xff, 0x9d, 0x0a, 0x6b, 0x6e, 0x82, - 0xfe, 0x87, 0x06, 0xd6, 0x8a, 0x33, 0x23, 0xdb, 0x1d, 0x19, 0xcb, 0xf2, 0xc6, 0xfd, 0xf2, 0xbf, - 0x6e, 0x5c, 0xca, 0xe1, 0xea, 0xd4, 0xd5, 0x1c, 0x65, 0x1c, 0x76, 0xab, 0x3d, 0x44, 0xe6, 0x68, - 0xfe, 0x9d, 0xdb, 0xbc, 0x21, 0x13, 0x37, 0x4e, 0xde, 0xb2, 0x8a, 0xad, 0xbe, 0x07, 0x16, 0x43, - 0x27, 0x89, 0x31, 0x32, 0xea, 0xb2, 0x9b, 0x3b, 0x29, 0x87, 0x39, 0x92, 0x71, 0xb8, 0x2a, 0x23, - 0xd5, 0xb2, 0x6d, 0xe5, 0xb8, 0xfe, 0x03, 0xd8, 0x70, 0x06, 0x03, 0xfa, 0x02, 0x23, 0x3b, 0xc0, - 0xec, 0x05, 0x8d, 0xce, 0x62, 0x03, 0xc8, 0x2b, 0xf5, 0x75, 0xca, 0x61, 0x23, 0xe7, 0x9e, 0xe6, - 0x54, 0xf1, 0x46, 0x54, 0xf1, 0xea, 0xa0, 0x19, 0xf3, 0x48, 0x6b, 0xd6, 0x4e, 0xff, 0x0e, 0x6c, - 0x39, 0x09, 0xa3, 0xb6, 0xe3, 0x79, 0x38, 0x64, 0xf6, 0x29, 0x1d, 0x20, 0x1c, 0xc5, 0xc6, 0x8a, - 0x3c, 0xfe, 0x87, 0x29, 0x87, 0x9b, 0x82, 0xfe, 0x5c, 0xb2, 0x5f, 0x28, 0x32, 0xe3, 0xf0, 0xae, - 0x3a, 0xc2, 0x2c, 0xd3, 0xb6, 0x6e, 0xaa, 0xf5, 0x67, 0x60, 0xcd, 0x77, 0xce, 0xed, 0x18, 0x07, - 0xc8, 0x3e, 0x73, 0xc3, 0xd8, 0x58, 0x6d, 0x69, 0xdd, 0xdb, 0xe6, 0x07, 0xe2, 0x72, 0xfa, 0xce, - 0xf9, 0x37, 0x38, 0x40, 0x47, 0x6e, 0x28, 0x5c, 0x37, 0xa5, 0x6b, 0x09, 0x6b, 0xbf, 0xe1, 0x70, - 0x81, 0x04, 0xcc, 0x2a, 0x0b, 0x27, 0x86, 0x11, 0xf6, 0x86, 0xca, 0x70, 0xad, 0x62, 0x68, 0x61, - 0x6f, 0x38, 0x6b, 0x38, 0xc1, 0x2a, 0x86, 0x13, 0x50, 0x0f, 0x40, 0x83, 0xf4, 0x03, 0x1a, 0x61, - 0x54, 0xd4, 0xbf, 0xde, 0x5a, 0xe8, 0xae, 0xec, 0x6d, 0xf7, 0xd4, 0xbf, 0x46, 0xef, 0x59, 0xfe, - 0xaf, 0xa1, 0x6a, 0x32, 0x1f, 0x89, 0x59, 0x4c, 0x39, 0x5c, 0xcf, 0xb7, 0x4d, 0x1b, 0xb3, 0xa5, - 0xa6, 0xaa, 0x0c, 0xb7, 0xad, 0x19, 0x99, 0xfe, 0x93, 0x06, 0x1a, 0x21, 0x0e, 0x10, 0x09, 0xfa, - 0x45, 0x60, 0xe3, 0x9d, 0x81, 0x4f, 0x44, 0xe0, 0x15, 0x87, 0xc6, 0x01, 0x0e, 0x23, 0xec, 0x39, - 0x0c, 0xa3, 0x63, 0x65, 0x90, 0x7b, 0xa6, 0x1c, 0x6a, 0x8f, 0x8a, 0x37, 0x28, 0x2c, 0x73, 0xa5, - 0xd1, 0x30, 0x34, 0x6b, 0xbd, 0xc2, 0xc5, 0xfa, 0xaf, 0x1a, 0x68, 0xa8, 0x6e, 0x7e, 0x9f, 0xe0, - 0x98, 0xd9, 0x67, 0xc4, 0x35, 0x36, 0x64, 0x3f, 0xe3, 0x2b, 0x0e, 0xd7, 0xbe, 0x12, 0x6d, 0x92, - 0xcc, 0x11, 0x31, 0x53, 0x0e, 0xd7, 0xfc, 0x32, 0x50, 0x14, 0x5c, 0x41, 0x27, 0x4d, 0x4e, 0x2f, - 0x3b, 0x33, 0xf2, 0x59, 0xe0, 0xe5, 0xb8, 0x53, 0x4d, 0xb0, 0x2a, 0xbc, 0xab, 0x7f, 0x06, 0xea, - 0x49, 0xc0, 0xa2, 0x24, 0x66, 0x18, 0x19, 0x9b, 0x72, 0x26, 0x5b, 0xe2, 0x7f, 0xa6, 0x00, 0x33, - 0x0e, 0x1b, 0xf2, 0x04, 0x05, 0xd2, 0xb6, 0xa6, 0xac, 0xac, 0x4e, 0x3c, 0x70, 0x0c, 0xdb, 0xfd, - 0x84, 0xd8, 0x21, 0x8d, 0x98, 0xa1, 0x4f, 0xab, 0xb3, 0x24, 0xf5, 0xe5, 0xb7, 0x87, 0xc7, 0x34, - 0x62, 0xa2, 0xba, 0xa8, 0x0c, 0x14, 0xd5, 0x55, 0xd0, 0x72, 0x75, 0x55, 0xf9, 0x2c, 0x20, 0xaa, - 0xab, 0x24, 0x58, 0x13, 0x3e, 0x21, 0x62, 0x69, 0x1e, 0x5d, 0xbc, 0x6e, 0xd6, 0xc6, 0xaf, 0x9b, - 0xb5, 0x8b, 0xab, 0xa6, 0x36, 0xbe, 0x6a, 0x6a, 0x3f, 0x5f, 0x37, 0x6b, 0xaf, 0xae, 0x9b, 0xda, - 0xf8, 0xba, 0x59, 0xfb, 0xeb, 0xba, 0x59, 0x3b, 0x79, 0xf0, 0x1f, 0x1e, 0x3b, 0x35, 0x31, 0xee, - 0xa2, 0x7c, 0xf4, 0x3e, 0xfa, 0x37, 0x00, 0x00, 0xff, 0xff, 0xbf, 0x4a, 0x4f, 0x60, 0x33, 0x09, - 0x00, 0x00, + 0x14, 0x16, 0xeb, 0xc4, 0xb1, 0xce, 0x3f, 0x64, 0x51, 0x8d, 0xc3, 0x18, 0x88, 0x4e, 0x50, 0x35, + 0x28, 0x68, 0x22, 0x17, 0x6e, 0x27, 0xf7, 0x07, 0x50, 0xda, 0x68, 0x63, 0x18, 0x4d, 0x5c, 0x16, + 0x5d, 0xbc, 0xb0, 0x14, 0xef, 0xac, 0x1c, 0x2c, 0xde, 0xb1, 0xe4, 0x51, 0xb1, 0x80, 0xfe, 0x01, + 0xed, 0x56, 0x04, 0xe8, 0xd4, 0x25, 0xed, 0xbf, 0xd1, 0xa1, 0xab, 0x37, 0x6b, 0x2c, 0x3a, 0x5c, + 0x11, 0x7b, 0xe3, 0xc8, 0x31, 0x53, 0x71, 0x47, 0x8a, 0x22, 0x65, 0x3b, 0x28, 0xd0, 0x8d, 0xf7, + 0x7d, 0xef, 0xbe, 0xef, 0xbd, 0xc7, 0x77, 0x77, 0xa0, 0x33, 0x24, 0xfd, 0x2d, 0x97, 0xd1, 0x63, + 0x32, 0xd8, 0x42, 0x78, 0x44, 0x5c, 0x9c, 0x2e, 0xa2, 0xc0, 0xe1, 0x84, 0xd1, 0x9e, 0x1f, 0x30, + 0xce, 0xf4, 0xc5, 0x14, 0xdc, 0xdc, 0x90, 0xd1, 0x0a, 0x72, 0xd9, 0x70, 0xab, 0x8f, 0xfd, 0x94, + 0xdf, 0xbc, 0x5f, 0x50, 0x61, 0xfd, 0x10, 0x07, 0x23, 0x8c, 0x32, 0xaa, 0x8a, 0x4f, 0x79, 0xfa, + 0xd9, 0xfe, 0xa7, 0x0e, 0x1a, 0x7b, 0xca, 0x63, 0xb7, 0xe8, 0xa1, 0xff, 0xa9, 0x81, 0x6a, 0xea, + 0x6d, 0x13, 0x64, 0x68, 0x2d, 0xad, 0xbb, 0x62, 0xfe, 0xa6, 0x9d, 0x09, 0x58, 0xf9, 0x5b, 0xc0, + 0x8f, 0x06, 0x84, 0x3f, 0x8f, 0xfa, 0x3d, 0x97, 0x79, 0x5b, 0xe1, 0x98, 0xba, 0xfc, 0x39, 0xa1, + 0x83, 0xc2, 0x57, 0x31, 0xa3, 0x5e, 0xaa, 0xbe, 0xbf, 0x77, 0x21, 0xe0, 0xd2, 0xf4, 0x3b, 0x16, + 0x70, 0x09, 0x65, 0xdf, 0x89, 0x80, 0xcd, 0x53, 0x6f, 0xb8, 0xd3, 0x26, 0xe8, 0x91, 0xc3, 0x79, + 0xd0, 0x6e, 0x51, 0x86, 0xf0, 0xb1, 0x13, 0x0d, 0xf9, 0x4e, 0x9b, 0x07, 0x11, 0x6e, 0xc7, 0xe7, + 0x9d, 0x3b, 0x19, 0x99, 0x9c, 0x77, 0xf2, 0x8d, 0x3f, 0x4e, 0x3a, 0xda, 0xcb, 0x49, 0x27, 0x17, + 0x7d, 0x35, 0xe9, 0x68, 0xd6, 0x94, 0x45, 0xfa, 0x21, 0xb8, 0x45, 0x1d, 0x0f, 0x1b, 0xef, 0xb4, + 0xb4, 0x6e, 0xd5, 0xfc, 0x24, 0x16, 0x50, 0xad, 0x13, 0x01, 0xef, 0x2b, 0x3b, 0xb9, 0x50, 0x9a, + 0x8f, 0x98, 0x47, 0x38, 0xf6, 0x7c, 0x3e, 0x96, 0x4e, 0x8d, 0x6b, 0x70, 0x4b, 0xed, 0xd4, 0x4f, + 0x41, 0xd5, 0x41, 0x28, 0xc0, 0x61, 0x88, 0x43, 0x63, 0xa1, 0xb5, 0xd0, 0xad, 0x9a, 0x47, 0xb1, + 0x80, 0x33, 0x30, 0x11, 0xf0, 0xa1, 0xd2, 0xce, 0x90, 0x82, 0x72, 0x2b, 0x2f, 0x09, 0x8d, 0xa9, + 0xe3, 0x11, 0x57, 0x7a, 0xd5, 0xaf, 0xc4, 0xbd, 0x39, 0xef, 0xdc, 0xc9, 0x02, 0xac, 0x99, 0xae, + 0x3e, 0x02, 0xcb, 0x2e, 0xf3, 0x7c, 0xb9, 0x22, 0x8c, 0x1a, 0xb7, 0x5a, 0x5a, 0x77, 0x6d, 0xfb, + 0x6e, 0x2f, 0xef, 0xf1, 0xee, 0x8c, 0x34, 0x3f, 0x8d, 0x05, 0x2c, 0x46, 0x27, 0x02, 0x6e, 0xa8, + 0xa4, 0x0a, 0x58, 0xda, 0xe8, 0xf8, 0xbc, 0xb3, 0x3e, 0x0f, 0x5a, 0xc5, 0xad, 0x3a, 0x06, 0x55, + 0x17, 0x07, 0xdc, 0x56, 0x8d, 0xbc, 0xad, 0x1a, 0xf9, 0x44, 0xfe, 0x3b, 0x09, 0x3e, 0x4d, 0x9b, + 0xf9, 0x20, 0xd5, 0xce, 0x80, 0x6b, 0x1a, 0x7a, 0xef, 0x06, 0xce, 0xca, 0x55, 0xf4, 0x23, 0x00, + 0x08, 0xe5, 0x01, 0x43, 0x91, 0x8b, 0x03, 0x63, 0xb1, 0xa5, 0x75, 0x97, 0xcc, 0x9d, 0x58, 0xc0, + 0x02, 0x9a, 0x08, 0x78, 0x37, 0x9d, 0x92, 0x1c, 0xca, 0x8b, 0xa8, 0xcd, 0x61, 0x56, 0x61, 0x9f, + 0xfe, 0xbb, 0x06, 0x36, 0xc3, 0x13, 0xe2, 0xdb, 0x53, 0x4c, 0x8e, 0xb7, 0x1d, 0x60, 0x8f, 0x8d, + 0x9c, 0x61, 0x68, 0xdc, 0x51, 0x66, 0x28, 0x16, 0xd0, 0x90, 0x51, 0xfb, 0x85, 0x20, 0x2b, 0x8b, + 0x49, 0x04, 0x7c, 0x4f, 0x59, 0xdf, 0x14, 0x90, 0x27, 0xf2, 0xe0, 0xad, 0x11, 0xd6, 0x8d, 0x0e, + 0xfa, 0x1f, 0x1a, 0x58, 0xcd, 0x73, 0x46, 0x76, 0x7f, 0x6c, 0x2c, 0xa9, 0x13, 0xf7, 0xcb, 0xff, + 0x3a, 0x71, 0xb1, 0x80, 0x2b, 0x33, 0x55, 0x73, 0x9c, 0x08, 0xd8, 0x2d, 0xf7, 0x10, 0x99, 0xe3, + 0x9b, 0xcf, 0x5c, 0xfd, 0x4a, 0x98, 0x3c, 0x71, 0xea, 0x94, 0x95, 0x64, 0xf5, 0x6d, 0xb0, 0xe8, + 0x3b, 0x51, 0x88, 0x91, 0x51, 0x55, 0xdd, 0xdc, 0x8c, 0x05, 0xcc, 0x90, 0x44, 0xc0, 0x15, 0x65, + 0x99, 0x2e, 0xdb, 0x56, 0x86, 0xeb, 0x3f, 0x80, 0x75, 0x67, 0x38, 0x64, 0x2f, 0x30, 0xb2, 0x29, + 0xe6, 0x2f, 0x58, 0x70, 0x12, 0x1a, 0x40, 0x1d, 0xa9, 0xaf, 0x63, 0x01, 0x6b, 0x19, 0xf7, 0x34, + 0xa3, 0xf2, 0x3b, 0xa2, 0x8c, 0x97, 0x07, 0xcd, 0xb8, 0x89, 0xb4, 0xe6, 0xe5, 0xf4, 0xef, 0x40, + 0xc3, 0x89, 0x38, 0xb3, 0x1d, 0xd7, 0xc5, 0x3e, 0xb7, 0x8f, 0xd9, 0x10, 0xe1, 0x20, 0x34, 0x96, + 0x55, 0xfa, 0x1f, 0xc4, 0x02, 0xd6, 0x25, 0xfd, 0xb9, 0x62, 0xbf, 0x48, 0xc9, 0x44, 0xc0, 0x7b, + 0x69, 0x0a, 0xf3, 0x4c, 0xdb, 0xba, 0x1a, 0xad, 0x3f, 0x03, 0xab, 0x9e, 0x73, 0x6a, 0x87, 0x98, + 0x22, 0xfb, 0xa4, 0xef, 0x87, 0xc6, 0x4a, 0x4b, 0xeb, 0xde, 0x36, 0xdf, 0x97, 0x87, 0xd3, 0x73, + 0x4e, 0xbf, 0xc1, 0x14, 0x1d, 0xf4, 0x7d, 0xa9, 0x5a, 0x57, 0xaa, 0x05, 0xac, 0xfd, 0x46, 0xc0, + 0x05, 0x42, 0xb9, 0x55, 0x0c, 0x9c, 0x0a, 0x06, 0xd8, 0x1d, 0xa5, 0x82, 0xab, 0x25, 0x41, 0x0b, + 0xbb, 0xa3, 0x79, 0xc1, 0x29, 0x56, 0x12, 0x9c, 0x82, 0x3a, 0x05, 0x35, 0x32, 0xa0, 0x2c, 0xc0, + 0x28, 0xaf, 0x7f, 0xad, 0xb5, 0xd0, 0x5d, 0xde, 0xde, 0xe8, 0xa5, 0xaf, 0x46, 0xef, 0x59, 0xf6, + 0x6a, 0xa4, 0x35, 0x99, 0x8f, 0xe5, 0x2c, 0xc6, 0x02, 0xae, 0x65, 0xdb, 0x66, 0x8d, 0x69, 0xa4, + 0x53, 0x55, 0x84, 0xdb, 0xd6, 0x5c, 0x98, 0xfe, 0x93, 0x06, 0x6a, 0x3e, 0xa6, 0x88, 0xd0, 0x41, + 0x6e, 0x58, 0x7b, 0xab, 0xe1, 0x13, 0x69, 0x78, 0x21, 0xa0, 0xb1, 0x87, 0xfd, 0x00, 0xbb, 0x0e, + 0xc7, 0xe8, 0x30, 0x15, 0xc8, 0x34, 0x63, 0x01, 0xb5, 0xc7, 0xf9, 0x1d, 0xe4, 0x17, 0xb9, 0xc2, + 0x68, 0x18, 0x9a, 0xb5, 0x56, 0xe2, 0x42, 0xfd, 0x57, 0x0d, 0xd4, 0xd2, 0x6e, 0x7e, 0x1f, 0xe1, + 0x90, 0xdb, 0x27, 0xa4, 0x6f, 0xac, 0xab, 0x7e, 0x86, 0x17, 0x02, 0xae, 0x7e, 0x25, 0xdb, 0xa4, + 0x98, 0x03, 0x62, 0xc6, 0x02, 0xae, 0x7a, 0x45, 0x20, 0x2f, 0xb8, 0x84, 0x4e, 0x9b, 0x1c, 0x9f, + 0x77, 0xe6, 0xc2, 0xe7, 0x81, 0x97, 0x93, 0x4e, 0xd9, 0xc1, 0x2a, 0xf1, 0x7d, 0xfd, 0x33, 0x50, + 0x8d, 0x28, 0x0f, 0xa2, 0x90, 0x63, 0x64, 0xd4, 0xd5, 0x4c, 0xb6, 0xe4, 0x3b, 0x93, 0x83, 0x89, + 0x80, 0x35, 0x95, 0x41, 0x8e, 0xb4, 0xad, 0x19, 0xab, 0xaa, 0x93, 0x17, 0x1c, 0xc7, 0xf6, 0x20, + 0x22, 0xb6, 0xcf, 0x02, 0x6e, 0xe8, 0xb3, 0xea, 0x2c, 0x45, 0x7d, 0xf9, 0xed, 0xfe, 0x21, 0x0b, + 0xb8, 0xac, 0x2e, 0x28, 0x02, 0x79, 0x75, 0x25, 0xb4, 0x58, 0x5d, 0x39, 0x7c, 0x1e, 0x90, 0xd5, + 0x95, 0x1c, 0xac, 0x29, 0x1f, 0x11, 0xb9, 0xd4, 0x29, 0x78, 0xd7, 0x8b, 0x86, 0x9c, 0xf8, 0x43, + 0x6c, 0xbb, 0x8c, 0x52, 0xac, 0xae, 0xc2, 0xd0, 0x68, 0xa8, 0x0c, 0x3f, 0x8e, 0x05, 0x6c, 0x4c, + 0xf9, 0xdd, 0x19, 0x9d, 0x3f, 0xdb, 0xd7, 0x70, 0xf9, 0x7c, 0x5f, 0xb7, 0xd1, 0x3c, 0x38, 0x7b, + 0xdd, 0xac, 0x4c, 0x5e, 0x37, 0x2b, 0x67, 0x17, 0x4d, 0x6d, 0x72, 0xd1, 0xd4, 0x7e, 0xbe, 0x6c, + 0x56, 0x5e, 0x5d, 0x36, 0xb5, 0xc9, 0x65, 0xb3, 0xf2, 0xd7, 0x65, 0xb3, 0x72, 0xf4, 0xf0, 0x3f, + 0x5c, 0xae, 0xe9, 0x84, 0xf6, 0x17, 0xd5, 0x25, 0xfb, 0xe1, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, + 0xd1, 0x8c, 0x1d, 0x62, 0xa3, 0x09, 0x00, 0x00, } func (m *DeviceConfiguration) Marshal() (dAtA []byte, err error) { @@ -176,6 +179,13 @@ func (m *DeviceConfiguration) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.MultipleConnections != 0 { + i = encodeVarintDeviceconfiguration(dAtA, i, uint64(m.MultipleConnections)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x98 + } if m.RemoteGUIPort != 0 { i = encodeVarintDeviceconfiguration(dAtA, i, uint64(m.RemoteGUIPort)) i-- @@ -423,6 +433,9 @@ func (m *DeviceConfiguration) ProtoSize() (n int) { if m.RemoteGUIPort != 0 { n += 2 + sovDeviceconfiguration(uint64(m.RemoteGUIPort)) } + if m.MultipleConnections != 0 { + n += 2 + sovDeviceconfiguration(uint64(m.MultipleConnections)) + } return n } @@ -918,6 +931,25 @@ func (m *DeviceConfiguration) Unmarshal(dAtA []byte) error { break } } + case 19: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MultipleConnections", wireType) + } + m.MultipleConnections = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDeviceconfiguration + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MultipleConnections |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipDeviceconfiguration(dAtA[iNdEx:]) diff --git a/lib/connections/service.go b/lib/connections/service.go index 55118f8fb..4daccc735 100644 --- a/lib/connections/service.go +++ b/lib/connections/service.go @@ -171,6 +171,9 @@ type service struct { listenersMut sync.RWMutex listeners map[string]genericListener listenerTokens map[string]suture.ServiceToken + + connectionsMut sync.Mutex + connections map[protocol.DeviceID]int } func NewService(cfg config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *tls.Config, discoverer discover.Finder, bepProtocolName string, tlsDefaultCommonName string, evLogger events.Logger, registry *registry.Registry, keyGen *protocol.KeyGenerator) Service { @@ -202,6 +205,9 @@ func NewService(cfg config.Wrapper, myID protocol.DeviceID, mdl Model, tlsCfg *t listenersMut: sync.NewRWMutex(), listeners: make(map[string]genericListener), listenerTokens: make(map[string]suture.ServiceToken), + + connectionsMut: sync.NewMutex(), + connections: make(map[protocol.DeviceID]int), } cfg.Subscribe(service) @@ -316,7 +322,7 @@ func (s *service) connectionCheckEarly(remoteID protocol.DeviceID, c internalCon if ct, ok := s.model.Connection(remoteID); ok { if ct.Priority() > c.priority || time.Since(ct.Statistics().StartedAt) > minConnectionReplaceAge { l.Debugf("Switching connections %s (existing: %s new: %s)", remoteID, ct, c) - } else { + } else if cfg.MultipleConnections <= s.connectionsForDevice(cfg.DeviceID) { // We should not already be connected to the other party. TODO: This // could use some better handling. If the old connection is dead but // hasn't timed out yet we may want to drop *that* connection and keep @@ -412,9 +418,16 @@ func (s *service) handleHellos(ctx context.Context) error { // connections are limited. rd, wr := s.limiter.getLimiters(remoteID, c, c.IsLocal()) + // Amazing hack: We need to pass the connection to the model, but we + // also need to pass the model to the connection. The + // connectionContextModel handles the mediation by being constructed + // in two stages... protoConn := protocol.NewConnection(remoteID, rd, wr, c, s.model, c, deviceCfg.Compression, s.cfg.FolderPasswords(remoteID), s.keyGen) + + s.accountAddedConnection(remoteID) go func() { <-protoConn.Closed() + s.accountRemovedConnection(remoteID) s.dialNowDevicesMut.Lock() s.dialNowDevices[remoteID] = struct{}{} s.scheduleDialNow() @@ -552,9 +565,10 @@ func (s *service) dialDevices(ctx context.Context, now time.Time, cfg config.Con // we don't attempt dialers that aren't considered a worthy upgrade. priorityCutoff -= cfg.Options.ConnectionPriorityUpgradeThreshold - if bestDialerPriority >= priorityCutoff { + if bestDialerPriority >= priorityCutoff && deviceCfg.MultipleConnections <= s.connectionsForDevice(deviceCfg.DeviceID) { // Our best dialer is not any better than what we already - // have, so nothing to do here. + // have, and we already have the desired number of + // connections to this device,so nothing to do here. continue } } @@ -666,7 +680,7 @@ func (s *service) resolveDialTargets(ctx context.Context, now time.Time, cfg con dialer := dialerFactory.New(s.cfg.Options(), s.tlsCfg, s.registry, s.lanChecker) priority := dialer.Priority(uri.Host) - if priority >= priorityCutoff { + if priority >= priorityCutoff && deviceCfg.MultipleConnections <= s.connectionsForDevice(deviceCfg.DeviceID) { l.Debugf("Not dialing using %s as priority is not better than current connection (%d >= %d)", dialerFactory, priority, priorityCutoff) continue } @@ -1183,6 +1197,24 @@ func (s *service) validateIdentity(c internalConn, expectedID protocol.DeviceID) return nil } +func (s *service) accountAddedConnection(d protocol.DeviceID) { + s.connectionsMut.Lock() + defer s.connectionsMut.Unlock() + s.connections[d]++ +} + +func (s *service) accountRemovedConnection(d protocol.DeviceID) { + s.connectionsMut.Lock() + defer s.connectionsMut.Unlock() + s.connections[d]-- +} + +func (s *service) connectionsForDevice(d protocol.DeviceID) int { + s.connectionsMut.Lock() + defer s.connectionsMut.Unlock() + return s.connections[d] +} + type nextDialRegistry map[protocol.DeviceID]nextDialDevice type nextDialDevice struct { diff --git a/lib/model/fakeconns_test.go b/lib/model/fakeconns_test.go index 171d527ec..06d7e8b0f 100644 --- a/lib/model/fakeconns_test.go +++ b/lib/model/fakeconns_test.go @@ -37,7 +37,7 @@ func newFakeConnection(id protocol.DeviceID, model Model) *fakeConnection { f.closeOnce.Do(func() { close(f.closed) }) - model.Closed(f.ConnectionID(), err) + model.Closed(f, err) f.ClosedReturns(f.closed) }) return f @@ -157,7 +157,7 @@ func (f *fakeConnection) sendIndexUpdate() { for i := range f.files { toSend[i] = prepareFileInfoForIndex(f.files[i]) } - f.model.IndexUpdate(f.id, f.folder, toSend) + f.model.IndexUpdate(f, f.folder, toSend) } func addFakeConn(m *testModel, dev protocol.DeviceID, folderID string) *fakeConnection { @@ -165,7 +165,7 @@ func addFakeConn(m *testModel, dev protocol.DeviceID, folderID string) *fakeConn fc.folder = folderID m.AddConnection(fc, protocol.Hello{}) - m.ClusterConfig(dev, protocol.ClusterConfig{ + m.ClusterConfig(fc, protocol.ClusterConfig{ Folders: []protocol.Folder{ { ID: folderID, diff --git a/lib/model/folder_recvonly_test.go b/lib/model/folder_recvonly_test.go index 918c3a6b1..41d771b05 100644 --- a/lib/model/folder_recvonly_test.go +++ b/lib/model/folder_recvonly_test.go @@ -45,7 +45,7 @@ func TestRecvOnlyRevertDeletes(t *testing.T) { // Send and index update for the known stuff - must(t, m.Index(device1, "ro", knownFiles)) + must(t, m.Index(device1Conn, "ro", knownFiles)) f.updateLocalsFromScanning(knownFiles) size := globalSize(t, m, "ro") @@ -122,7 +122,7 @@ func TestRecvOnlyRevertNeeds(t *testing.T) { // Send and index update for the known stuff - must(t, m.Index(device1, "ro", knownFiles)) + must(t, m.Index(device1Conn, "ro", knownFiles)) f.updateLocalsFromScanning(knownFiles) // Scan the folder. @@ -212,7 +212,7 @@ func TestRecvOnlyUndoChanges(t *testing.T) { // Send an index update for the known stuff - must(t, m.Index(device1, "ro", knownFiles)) + must(t, m.Index(device1Conn, "ro", knownFiles)) f.updateLocalsFromScanning(knownFiles) // Scan the folder. @@ -282,7 +282,7 @@ func TestRecvOnlyDeletedRemoteDrop(t *testing.T) { // Send an index update for the known stuff - must(t, m.Index(device1, "ro", knownFiles)) + must(t, m.Index(device1Conn, "ro", knownFiles)) f.updateLocalsFromScanning(knownFiles) // Scan the folder. @@ -347,7 +347,7 @@ func TestRecvOnlyRemoteUndoChanges(t *testing.T) { // Send an index update for the known stuff - must(t, m.Index(device1, "ro", knownFiles)) + must(t, m.Index(device1Conn, "ro", knownFiles)) f.updateLocalsFromScanning(knownFiles) // Scan the folder. @@ -402,7 +402,7 @@ func TestRecvOnlyRemoteUndoChanges(t *testing.T) { return true }) snap.Release() - must(t, m.IndexUpdate(device1, "ro", files)) + must(t, m.IndexUpdate(device1Conn, "ro", files)) // Ensure the pull to resolve conflicts (content identical) happened must(t, f.doInSync(func() error { @@ -470,7 +470,7 @@ func TestRecvOnlyRevertOwnID(t *testing.T) { }() // Receive an index update with an older version, but valid and then revert - must(t, m.Index(device1, f.ID, []protocol.FileInfo{fi})) + must(t, m.Index(device1Conn, f.ID, []protocol.FileInfo{fi})) f.Revert() select { diff --git a/lib/model/folder_sendrecv_test.go b/lib/model/folder_sendrecv_test.go index 36764560f..9b4b8bd29 100644 --- a/lib/model/folder_sendrecv_test.go +++ b/lib/model/folder_sendrecv_test.go @@ -1296,7 +1296,7 @@ func TestPullSymlinkOverExistingWindows(t *testing.T) { if !ok { t.Fatal("file missing") } - must(t, m.Index(device1, f.ID, []protocol.FileInfo{{Name: name, Type: protocol.FileInfoTypeSymlink, Version: file.Version.Update(device1.Short())}})) + must(t, m.Index(device1Conn, f.ID, []protocol.FileInfo{{Name: name, Type: protocol.FileInfoTypeSymlink, Version: file.Version.Update(device1.Short())}})) scanChan := make(chan string) diff --git a/lib/model/mocks/model.go b/lib/model/mocks/model.go index 43187c5ba..c0f8c95b8 100644 --- a/lib/model/mocks/model.go +++ b/lib/model/mocks/model.go @@ -44,16 +44,16 @@ type Model struct { arg1 string arg2 string } - ClosedStub func(string, error) + ClosedStub func(protocol.Connection, error) closedMutex sync.RWMutex closedArgsForCall []struct { - arg1 string + arg1 protocol.Connection arg2 error } - ClusterConfigStub func(protocol.DeviceID, protocol.ClusterConfig) error + ClusterConfigStub func(protocol.Connection, protocol.ClusterConfig) error clusterConfigMutex sync.RWMutex clusterConfigArgsForCall []struct { - arg1 protocol.DeviceID + arg1 protocol.Connection arg2 protocol.ClusterConfig } clusterConfigReturns struct { @@ -200,10 +200,10 @@ type Model struct { dismissPendingFolderReturnsOnCall map[int]struct { result1 error } - DownloadProgressStub func(protocol.DeviceID, string, []protocol.FileDownloadProgressUpdate) error + DownloadProgressStub func(protocol.Connection, string, []protocol.FileDownloadProgressUpdate) error downloadProgressMutex sync.RWMutex downloadProgressArgsForCall []struct { - arg1 protocol.DeviceID + arg1 protocol.Connection arg2 string arg3 []protocol.FileDownloadProgressUpdate } @@ -303,10 +303,10 @@ type Model struct { result1 []*model.TreeEntry result2 error } - IndexStub func(protocol.DeviceID, string, []protocol.FileInfo) error + IndexStub func(protocol.Connection, string, []protocol.FileInfo) error indexMutex sync.RWMutex indexArgsForCall []struct { - arg1 protocol.DeviceID + arg1 protocol.Connection arg2 string arg3 []protocol.FileInfo } @@ -316,10 +316,10 @@ type Model struct { indexReturnsOnCall map[int]struct { result1 error } - IndexUpdateStub func(protocol.DeviceID, string, []protocol.FileInfo) error + IndexUpdateStub func(protocol.Connection, string, []protocol.FileInfo) error indexUpdateMutex sync.RWMutex indexUpdateArgsForCall []struct { - arg1 protocol.DeviceID + arg1 protocol.Connection arg2 string arg3 []protocol.FileInfo } @@ -447,10 +447,10 @@ type Model struct { result1 []db.FileInfoTruncated result2 error } - RequestStub func(protocol.DeviceID, string, string, int32, int32, int64, []byte, uint32, bool) (protocol.RequestResponse, error) + RequestStub func(protocol.Connection, string, string, int32, int32, int64, []byte, uint32, bool) (protocol.RequestResponse, error) requestMutex sync.RWMutex requestArgsForCall []struct { - arg1 protocol.DeviceID + arg1 protocol.Connection arg2 string arg3 string arg4 int32 @@ -728,10 +728,10 @@ func (fake *Model) BringToFrontArgsForCall(i int) (string, string) { return argsForCall.arg1, argsForCall.arg2 } -func (fake *Model) Closed(arg1 string, arg2 error) { +func (fake *Model) Closed(arg1 protocol.Connection, arg2 error) { fake.closedMutex.Lock() fake.closedArgsForCall = append(fake.closedArgsForCall, struct { - arg1 string + arg1 protocol.Connection arg2 error }{arg1, arg2}) stub := fake.ClosedStub @@ -748,24 +748,24 @@ func (fake *Model) ClosedCallCount() int { return len(fake.closedArgsForCall) } -func (fake *Model) ClosedCalls(stub func(string, error)) { +func (fake *Model) ClosedCalls(stub func(protocol.Connection, error)) { fake.closedMutex.Lock() defer fake.closedMutex.Unlock() fake.ClosedStub = stub } -func (fake *Model) ClosedArgsForCall(i int) (string, error) { +func (fake *Model) ClosedArgsForCall(i int) (protocol.Connection, error) { fake.closedMutex.RLock() defer fake.closedMutex.RUnlock() argsForCall := fake.closedArgsForCall[i] return argsForCall.arg1, argsForCall.arg2 } -func (fake *Model) ClusterConfig(arg1 protocol.DeviceID, arg2 protocol.ClusterConfig) error { +func (fake *Model) ClusterConfig(arg1 protocol.Connection, arg2 protocol.ClusterConfig) error { fake.clusterConfigMutex.Lock() ret, specificReturn := fake.clusterConfigReturnsOnCall[len(fake.clusterConfigArgsForCall)] fake.clusterConfigArgsForCall = append(fake.clusterConfigArgsForCall, struct { - arg1 protocol.DeviceID + arg1 protocol.Connection arg2 protocol.ClusterConfig }{arg1, arg2}) stub := fake.ClusterConfigStub @@ -787,13 +787,13 @@ func (fake *Model) ClusterConfigCallCount() int { return len(fake.clusterConfigArgsForCall) } -func (fake *Model) ClusterConfigCalls(stub func(protocol.DeviceID, protocol.ClusterConfig) error) { +func (fake *Model) ClusterConfigCalls(stub func(protocol.Connection, protocol.ClusterConfig) error) { fake.clusterConfigMutex.Lock() defer fake.clusterConfigMutex.Unlock() fake.ClusterConfigStub = stub } -func (fake *Model) ClusterConfigArgsForCall(i int) (protocol.DeviceID, protocol.ClusterConfig) { +func (fake *Model) ClusterConfigArgsForCall(i int) (protocol.Connection, protocol.ClusterConfig) { fake.clusterConfigMutex.RLock() defer fake.clusterConfigMutex.RUnlock() argsForCall := fake.clusterConfigArgsForCall[i] @@ -1484,7 +1484,7 @@ func (fake *Model) DismissPendingFolderReturnsOnCall(i int, result1 error) { }{result1} } -func (fake *Model) DownloadProgress(arg1 protocol.DeviceID, arg2 string, arg3 []protocol.FileDownloadProgressUpdate) error { +func (fake *Model) DownloadProgress(arg1 protocol.Connection, arg2 string, arg3 []protocol.FileDownloadProgressUpdate) error { var arg3Copy []protocol.FileDownloadProgressUpdate if arg3 != nil { arg3Copy = make([]protocol.FileDownloadProgressUpdate, len(arg3)) @@ -1493,7 +1493,7 @@ func (fake *Model) DownloadProgress(arg1 protocol.DeviceID, arg2 string, arg3 [] fake.downloadProgressMutex.Lock() ret, specificReturn := fake.downloadProgressReturnsOnCall[len(fake.downloadProgressArgsForCall)] fake.downloadProgressArgsForCall = append(fake.downloadProgressArgsForCall, struct { - arg1 protocol.DeviceID + arg1 protocol.Connection arg2 string arg3 []protocol.FileDownloadProgressUpdate }{arg1, arg2, arg3Copy}) @@ -1516,13 +1516,13 @@ func (fake *Model) DownloadProgressCallCount() int { return len(fake.downloadProgressArgsForCall) } -func (fake *Model) DownloadProgressCalls(stub func(protocol.DeviceID, string, []protocol.FileDownloadProgressUpdate) error) { +func (fake *Model) DownloadProgressCalls(stub func(protocol.Connection, string, []protocol.FileDownloadProgressUpdate) error) { fake.downloadProgressMutex.Lock() defer fake.downloadProgressMutex.Unlock() fake.DownloadProgressStub = stub } -func (fake *Model) DownloadProgressArgsForCall(i int) (protocol.DeviceID, string, []protocol.FileDownloadProgressUpdate) { +func (fake *Model) DownloadProgressArgsForCall(i int) (protocol.Connection, string, []protocol.FileDownloadProgressUpdate) { fake.downloadProgressMutex.RLock() defer fake.downloadProgressMutex.RUnlock() argsForCall := fake.downloadProgressArgsForCall[i] @@ -1990,7 +1990,7 @@ func (fake *Model) GlobalDirectoryTreeReturnsOnCall(i int, result1 []*model.Tree }{result1, result2} } -func (fake *Model) Index(arg1 protocol.DeviceID, arg2 string, arg3 []protocol.FileInfo) error { +func (fake *Model) Index(arg1 protocol.Connection, arg2 string, arg3 []protocol.FileInfo) error { var arg3Copy []protocol.FileInfo if arg3 != nil { arg3Copy = make([]protocol.FileInfo, len(arg3)) @@ -1999,7 +1999,7 @@ func (fake *Model) Index(arg1 protocol.DeviceID, arg2 string, arg3 []protocol.Fi fake.indexMutex.Lock() ret, specificReturn := fake.indexReturnsOnCall[len(fake.indexArgsForCall)] fake.indexArgsForCall = append(fake.indexArgsForCall, struct { - arg1 protocol.DeviceID + arg1 protocol.Connection arg2 string arg3 []protocol.FileInfo }{arg1, arg2, arg3Copy}) @@ -2022,13 +2022,13 @@ func (fake *Model) IndexCallCount() int { return len(fake.indexArgsForCall) } -func (fake *Model) IndexCalls(stub func(protocol.DeviceID, string, []protocol.FileInfo) error) { +func (fake *Model) IndexCalls(stub func(protocol.Connection, string, []protocol.FileInfo) error) { fake.indexMutex.Lock() defer fake.indexMutex.Unlock() fake.IndexStub = stub } -func (fake *Model) IndexArgsForCall(i int) (protocol.DeviceID, string, []protocol.FileInfo) { +func (fake *Model) IndexArgsForCall(i int) (protocol.Connection, string, []protocol.FileInfo) { fake.indexMutex.RLock() defer fake.indexMutex.RUnlock() argsForCall := fake.indexArgsForCall[i] @@ -2058,7 +2058,7 @@ func (fake *Model) IndexReturnsOnCall(i int, result1 error) { }{result1} } -func (fake *Model) IndexUpdate(arg1 protocol.DeviceID, arg2 string, arg3 []protocol.FileInfo) error { +func (fake *Model) IndexUpdate(arg1 protocol.Connection, arg2 string, arg3 []protocol.FileInfo) error { var arg3Copy []protocol.FileInfo if arg3 != nil { arg3Copy = make([]protocol.FileInfo, len(arg3)) @@ -2067,7 +2067,7 @@ func (fake *Model) IndexUpdate(arg1 protocol.DeviceID, arg2 string, arg3 []proto fake.indexUpdateMutex.Lock() ret, specificReturn := fake.indexUpdateReturnsOnCall[len(fake.indexUpdateArgsForCall)] fake.indexUpdateArgsForCall = append(fake.indexUpdateArgsForCall, struct { - arg1 protocol.DeviceID + arg1 protocol.Connection arg2 string arg3 []protocol.FileInfo }{arg1, arg2, arg3Copy}) @@ -2090,13 +2090,13 @@ func (fake *Model) IndexUpdateCallCount() int { return len(fake.indexUpdateArgsForCall) } -func (fake *Model) IndexUpdateCalls(stub func(protocol.DeviceID, string, []protocol.FileInfo) error) { +func (fake *Model) IndexUpdateCalls(stub func(protocol.Connection, string, []protocol.FileInfo) error) { fake.indexUpdateMutex.Lock() defer fake.indexUpdateMutex.Unlock() fake.IndexUpdateStub = stub } -func (fake *Model) IndexUpdateArgsForCall(i int) (protocol.DeviceID, string, []protocol.FileInfo) { +func (fake *Model) IndexUpdateArgsForCall(i int) (protocol.Connection, string, []protocol.FileInfo) { fake.indexUpdateMutex.RLock() defer fake.indexUpdateMutex.RUnlock() argsForCall := fake.indexUpdateArgsForCall[i] @@ -2666,7 +2666,7 @@ func (fake *Model) RemoteNeedFolderFilesReturnsOnCall(i int, result1 []db.FileIn }{result1, result2} } -func (fake *Model) Request(arg1 protocol.DeviceID, arg2 string, arg3 string, arg4 int32, arg5 int32, arg6 int64, arg7 []byte, arg8 uint32, arg9 bool) (protocol.RequestResponse, error) { +func (fake *Model) Request(arg1 protocol.Connection, arg2 string, arg3 string, arg4 int32, arg5 int32, arg6 int64, arg7 []byte, arg8 uint32, arg9 bool) (protocol.RequestResponse, error) { var arg7Copy []byte if arg7 != nil { arg7Copy = make([]byte, len(arg7)) @@ -2675,7 +2675,7 @@ func (fake *Model) Request(arg1 protocol.DeviceID, arg2 string, arg3 string, arg fake.requestMutex.Lock() ret, specificReturn := fake.requestReturnsOnCall[len(fake.requestArgsForCall)] fake.requestArgsForCall = append(fake.requestArgsForCall, struct { - arg1 protocol.DeviceID + arg1 protocol.Connection arg2 string arg3 string arg4 int32 @@ -2704,13 +2704,13 @@ func (fake *Model) RequestCallCount() int { return len(fake.requestArgsForCall) } -func (fake *Model) RequestCalls(stub func(protocol.DeviceID, string, string, int32, int32, int64, []byte, uint32, bool) (protocol.RequestResponse, error)) { +func (fake *Model) RequestCalls(stub func(protocol.Connection, string, string, int32, int32, int64, []byte, uint32, bool) (protocol.RequestResponse, error)) { fake.requestMutex.Lock() defer fake.requestMutex.Unlock() fake.RequestStub = stub } -func (fake *Model) RequestArgsForCall(i int) (protocol.DeviceID, string, string, int32, int32, int64, []byte, uint32, bool) { +func (fake *Model) RequestArgsForCall(i int) (protocol.Connection, string, string, int32, int32, int64, []byte, uint32, bool) { fake.requestMutex.RLock() defer fake.requestMutex.RUnlock() argsForCall := fake.requestArgsForCall[i] diff --git a/lib/model/model.go b/lib/model/model.go index 735a5dade..95011b657 100644 --- a/lib/model/model.go +++ b/lib/model/model.go @@ -1120,14 +1120,14 @@ func (p *pager) done() bool { // Index is called when a new device is connected and we receive their full index. // Implements the protocol.Model interface. -func (m *model) Index(deviceID protocol.DeviceID, folder string, fs []protocol.FileInfo) error { - return m.handleIndex(deviceID, folder, fs, false) +func (m *model) Index(conn protocol.Connection, folder string, fs []protocol.FileInfo) error { + return m.handleIndex(conn.ID(), folder, fs, false) } // IndexUpdate is called for incremental updates to connected devices' indexes. // Implements the protocol.Model interface. -func (m *model) IndexUpdate(deviceID protocol.DeviceID, folder string, fs []protocol.FileInfo) error { - return m.handleIndex(deviceID, folder, fs, true) +func (m *model) IndexUpdate(conn protocol.Connection, folder string, fs []protocol.FileInfo) error { + return m.handleIndex(conn.ID(), folder, fs, true) } func (m *model) handleIndex(deviceID protocol.DeviceID, folder string, fs []protocol.FileInfo, update bool) error { @@ -1169,13 +1169,20 @@ type ClusterConfigReceivedEventData struct { Device protocol.DeviceID `json:"device"` } -func (m *model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterConfig) error { +func (m *model) ClusterConfig(conn protocol.Connection, cm protocol.ClusterConfig) error { + if cm.Secondary { + // No handling of secondary connection ClusterConfigs; they merely + // indicate the connection is ready to start. + return nil + } + // Check the peer device's announced folders against our own. Emits events // for folders that we don't expect (unknown or not shared). // Also, collect a list of folders we do share, and if he's interested in // temporary indexes, subscribe the connection. - l.Debugf("Handling ClusterConfig from %v", deviceID.Short()) + deviceID := conn.ID() + l.Debugf("Handling ClusterConfig from %v/%s", deviceID.Short(), conn.ConnectionID()) m.pmut.RLock() indexHandlerRegistry, ok := m.indexHandlers[deviceID] @@ -1788,7 +1795,10 @@ func (m *model) introduceDevice(device protocol.Device, introducerCfg config.Dev } // Closed is called when a connection has been closed -func (m *model) Closed(connID string, err error) { +func (m *model) Closed(conn protocol.Connection, err error) { + connID := conn.ConnectionID() + deviceID := conn.ID() + m.pmut.Lock() conn, ok := m.conns[connID] if !ok { @@ -1800,32 +1810,31 @@ func (m *model) Closed(connID string, err error) { delete(m.closed, connID) delete(m.conns, connID) - device := conn.ID() - removedIsPrimary := m.deviceConns[device][0] == connID - remainingConns := without(m.deviceConns[device], connID) + removedIsPrimary := m.deviceConns[deviceID][0] == connID + remainingConns := without(m.deviceConns[deviceID], connID) // XXX: all the below needs more thinking about when to remove what if removedIsPrimary { m.progressEmitter.temporaryIndexUnsubscribe(conn) - delete(m.indexHandlers, device) - delete(m.deviceDownloads, device) + delete(m.indexHandlers, deviceID) + delete(m.deviceDownloads, deviceID) m.scheduleConnectionPromotion() } if len(remainingConns) == 0 { // All device connections closed - delete(m.deviceConns, device) - delete(m.connRequestLimiters, device) - delete(m.helloMessages, device) - delete(m.remoteFolderStates, device) - m.deviceDidClose(device, time.Since(conn.EstablishedAt())) + delete(m.deviceConns, deviceID) + delete(m.connRequestLimiters, deviceID) + delete(m.helloMessages, deviceID) + delete(m.remoteFolderStates, deviceID) + m.deviceDidClose(deviceID, time.Since(conn.EstablishedAt())) } else { // Some connections remain - m.deviceConns[device] = remainingConns + m.deviceConns[deviceID] = remainingConns } m.pmut.Unlock() - l.Infof("Connection to %s at %s closed: %v", device, conn, err) + l.Infof("Connection to %s at %s closed: %v", deviceID, conn, err) m.evLogger.Log(events.DeviceDisconnected, map[string]string{ - "id": device.String(), + "id": deviceID.String(), "error": err.Error(), }) close(closed) @@ -1862,11 +1871,14 @@ func (r *requestResponse) Wait() { // Request returns the specified data segment by reading it from local disk. // Implements the protocol.Model interface. -func (m *model) Request(deviceID protocol.DeviceID, folder, name string, _, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (out protocol.RequestResponse, err error) { +func (m *model) Request(conn protocol.Connection, folder, name string, _, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (out protocol.RequestResponse, err error) { if size < 0 || offset < 0 { return nil, protocol.ErrInvalid } + deviceID := conn.ID() + connID := conn.ConnectionID() + m.fmut.RLock() folderCfg, ok := m.folderCfgs[folder] folderIgnores := m.folderIgnores[folder] @@ -1894,16 +1906,16 @@ func (m *model) Request(deviceID protocol.DeviceID, folder, name string, _, size } if deviceID != protocol.LocalDeviceID { - l.Debugf("%v REQ(in): %s: %q / %q o=%d s=%d t=%v", m, deviceID, folder, name, offset, size, fromTemporary) + l.Debugf("%v REQ(in): %s/%s: %q / %q o=%d s=%d t=%v", m, deviceID, connID, folder, name, offset, size, fromTemporary) } if fs.IsInternal(name) { - l.Debugf("%v REQ(in) for internal file: %s: %q / %q o=%d s=%d", m, deviceID, folder, name, offset, size) + l.Debugf("%v REQ(in) for internal file: %s/%s: %q / %q o=%d s=%d", m, deviceID, connID, folder, name, offset, size) return nil, protocol.ErrInvalid } if folderIgnores.Match(name).IsIgnored() { - l.Debugf("%v REQ(in) for ignored file: %s: %q / %q o=%d s=%d", m, deviceID, folder, name, offset, size) + l.Debugf("%v REQ(in) for ignored file: %s/%s: %q / %q o=%d s=%d", m, deviceID, connID, folder, name, offset, size) return nil, protocol.ErrInvalid } @@ -2281,8 +2293,6 @@ func (m *model) AddConnection(conn protocol.Connection, hello protocol.Hello) { l.Infof(`Device %s client is "%s %s" named "%s" at %s`, deviceID, hello.ClientName, hello.ClientVersion, hello.DeviceName, conn) - conn.Start() - m.pmut.Unlock() if (deviceCfg.Name == "" || m.cfg.Options().OverwriteRemoteDevNames) && hello.DeviceName != "" { @@ -2319,11 +2329,29 @@ func (m *model) promoteConnections() { defer m.pmut.Unlock() for deviceID, connIDs := range m.deviceConns { + cm, passwords := m.generateClusterConfigFRLocked(deviceID) if _, ok := m.indexHandlers[deviceID]; !ok { - // Connected device lacks and index handler. We should promote + // Connected device lacks an index handler. We should promote // the primary connection to be the index handling one. l.Infoln("Promoting connection", connIDs[0], "to", deviceID) - m.promoteDeviceConnectionLocked(m.conns[connIDs[0]]) + conn := m.conns[connIDs[0]] + m.promoteDeviceConnectionLocked(conn) + if conn.Statistics().StartedAt.IsZero() { + conn.SetFolderPasswords(passwords) + conn.Start() + conn.ClusterConfig(cm) + } + } + + // Make sure any new connections also get started, and an empty + // cluster config. + for _, connID := range connIDs[1:] { + conn := m.conns[connID] + if conn.Statistics().StartedAt.IsZero() { + conn.SetFolderPasswords(passwords) + conn.Start() + conn.ClusterConfig(protocol.ClusterConfig{Secondary: true}) + } } } } @@ -2333,35 +2361,32 @@ func (m *model) promoteDeviceConnectionLocked(conn protocol.Connection) { connID := conn.ConnectionID() m.deviceDownloads[deviceID] = newDeviceDownloadState() - indexRegistry := newIndexHandlerRegistry(conn, m.deviceDownloads[deviceID], m.closed[connID], m.Supervisor, m.evLogger) for id, fcfg := range m.folderCfgs { indexRegistry.RegisterFolderState(fcfg, m.folderFiles[id], m.folderRunners[id]) } m.indexHandlers[deviceID] = indexRegistry - - cm, passwords := m.generateClusterConfigFRLocked(deviceID) - conn.SetFolderPasswords(passwords) - conn.ClusterConfig(cm) } -func (m *model) DownloadProgress(device protocol.DeviceID, folder string, updates []protocol.FileDownloadProgressUpdate) error { +func (m *model) DownloadProgress(conn protocol.Connection, folder string, updates []protocol.FileDownloadProgressUpdate) error { + deviceID := conn.ID() + m.fmut.RLock() cfg, ok := m.folderCfgs[folder] m.fmut.RUnlock() - if !ok || cfg.DisableTempIndexes || !cfg.SharedWith(device) { + if !ok || cfg.DisableTempIndexes || !cfg.SharedWith(deviceID) { return nil } m.pmut.RLock() - downloads := m.deviceDownloads[device] + downloads := m.deviceDownloads[deviceID] m.pmut.RUnlock() downloads.Update(folder, updates) state := downloads.GetBlockCounts(folder) m.evLogger.Log(events.RemoteDownloadProgress, map[string]interface{}{ - "device": device.String(), + "device": deviceID.String(), "folder": folder, "state": state, }) @@ -2406,7 +2431,7 @@ func (m *model) requestGlobal(ctx context.Context, deviceID protocol.DeviceID, f return nil, fmt.Errorf("requestGlobal: no such device: %s", deviceID) } - l.Debugf("%v REQ(out): %s: %q / %q b=%d o=%d s=%d h=%x wh=%x ft=%t", m, deviceID, folder, name, blockNo, offset, size, hash, weakHash, fromTemporary) + l.Debugf("%v REQ(out): %s (%s): %q / %q b=%d o=%d s=%d h=%x wh=%x ft=%t", m, deviceID, conn.String(), folder, name, blockNo, offset, size, hash, weakHash, fromTemporary) return conn.Request(ctx, folder, name, blockNo, offset, size, hash, weakHash, fromTemporary) } diff --git a/lib/model/model_test.go b/lib/model/model_test.go index 17ed3aec8..b8e518f8e 100644 --- a/lib/model/model_test.go +++ b/lib/model/model_test.go @@ -94,7 +94,7 @@ func TestRequest(t *testing.T) { m.ScanFolder("default") // Existing, shared file - res, err := m.Request(device1, "default", "foo", 0, 6, 0, nil, 0, false) + res, err := m.Request(device1Conn, "default", "foo", 0, 6, 0, nil, 0, false) if err != nil { t.Fatal(err) } @@ -104,35 +104,35 @@ func TestRequest(t *testing.T) { } // Existing, nonshared file - _, err = m.Request(device2, "default", "foo", 0, 6, 0, nil, 0, false) + _, err = m.Request(device2Conn, "default", "foo", 0, 6, 0, nil, 0, false) if err == nil { t.Error("Unexpected nil error on insecure file read") } // Nonexistent file - _, err = m.Request(device1, "default", "nonexistent", 0, 6, 0, nil, 0, false) + _, err = m.Request(device1Conn, "default", "nonexistent", 0, 6, 0, nil, 0, false) if err == nil { t.Error("Unexpected nil error on insecure file read") } // Shared folder, but disallowed file name - _, err = m.Request(device1, "default", "../walk.go", 0, 6, 0, nil, 0, false) + _, err = m.Request(device1Conn, "default", "../walk.go", 0, 6, 0, nil, 0, false) if err == nil { t.Error("Unexpected nil error on insecure file read") } // Negative offset - _, err = m.Request(device1, "default", "foo", 0, -4, 0, nil, 0, false) + _, err = m.Request(device1Conn, "default", "foo", 0, -4, 0, nil, 0, false) if err == nil { t.Error("Unexpected nil error on insecure file read") } // Larger block than available - _, err = m.Request(device1, "default", "foo", 0, 42, 0, []byte("hash necessary but not checked"), 0, false) + _, err = m.Request(device1Conn, "default", "foo", 0, 42, 0, []byte("hash necessary but not checked"), 0, false) if err == nil { t.Error("Unexpected nil error on read past end of file") } - _, err = m.Request(device1, "default", "foo", 0, 42, 0, nil, 0, false) + _, err = m.Request(device1Conn, "default", "foo", 0, 42, 0, nil, 0, false) if err != nil { t.Error("Unexpected error when large read should be permitted") } @@ -168,11 +168,11 @@ func benchmarkIndex(b *testing.B, nfiles int) { defer cleanupModelAndRemoveDir(m, fcfg.Filesystem(nil).URI()) files := genFiles(nfiles) - must(b, m.Index(device1, fcfg.ID, files)) + must(b, m.Index(device1Conn, fcfg.ID, files)) b.ResetTimer() for i := 0; i < b.N; i++ { - must(b, m.Index(device1, fcfg.ID, files)) + must(b, m.Index(device1Conn, fcfg.ID, files)) } b.ReportAllocs() } @@ -197,11 +197,11 @@ func benchmarkIndexUpdate(b *testing.B, nfiles, nufiles int) { files := genFiles(nfiles) ufiles := genFiles(nufiles) - must(b, m.Index(device1, fcfg.ID, files)) + must(b, m.Index(device1Conn, fcfg.ID, files)) b.ResetTimer() for i := 0; i < b.N; i++ { - must(b, m.IndexUpdate(device1, fcfg.ID, ufiles)) + must(b, m.IndexUpdate(device1Conn, fcfg.ID, ufiles)) } b.ReportAllocs() } @@ -218,7 +218,7 @@ func BenchmarkRequestOut(b *testing.B) { fc.addFile(f.Name, 0o644, protocol.FileInfoTypeFile, []byte("some data to return")) } m.AddConnection(fc, protocol.Hello{}) - must(b, m.Index(device1, "default", files)) + must(b, m.Index(device1Conn, "default", files)) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -247,7 +247,7 @@ func BenchmarkRequestInSingleFile(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - if _, err := m.Request(device1, "default", "request/for/a/file/in/a/couple/of/dirs/128k", 0, 128<<10, 0, nil, 0, false); err != nil { + if _, err := m.Request(device1Conn, "default", "request/for/a/file/in/a/couple/of/dirs/128k", 0, 128<<10, 0, nil, 0, false); err != nil { b.Error(err) } } @@ -287,7 +287,7 @@ func TestDeviceRename(t *testing.T) { t.Errorf("Device already has a name") } - m.Closed(conn.ConnectionID(), protocol.ErrTimeout) + m.Closed(conn, protocol.ErrTimeout) hello.DeviceName = "tester" m.AddConnection(conn, hello) @@ -295,7 +295,7 @@ func TestDeviceRename(t *testing.T) { t.Errorf("Device did not get a name") } - m.Closed(conn.ConnectionID(), protocol.ErrTimeout) + m.Closed(conn, protocol.ErrTimeout) hello.DeviceName = "tester2" m.AddConnection(conn, hello) @@ -317,7 +317,7 @@ func TestDeviceRename(t *testing.T) { t.Errorf("Device name not saved in config") } - m.Closed(conn.ConnectionID(), protocol.ErrTimeout) + m.Closed(conn, protocol.ErrTimeout) waiter, err := cfg.Modify(func(cfg *config.Configuration) { cfg.Options.OverwriteRemoteDevNames = true @@ -528,7 +528,7 @@ func TestIntroducer(t *testing.T) { SkipIntroductionRemovals: true, EncryptionPasswordToken: []byte("faketoken"), }) - m.ClusterConfig(device1, cc) + m.ClusterConfig(device1Conn, cc) if newDev, ok := m.cfg.Device(device2); !ok || !newDev.Introducer || !newDev.SkipIntroductionRemovals { t.Error("device 2 missing or wrong flags") @@ -584,7 +584,7 @@ func TestIntroducer(t *testing.T) { Introducer: true, SkipIntroductionRemovals: true, }) - m.ClusterConfig(device1, cc) + m.ClusterConfig(device1Conn, cc) // Should not get introducer, as it's already unset, and it's an existing device. if newDev, ok := m.cfg.Device(device2); !ok || newDev.Introducer || newDev.SkipIntroductionRemovals { @@ -634,7 +634,7 @@ func TestIntroducer(t *testing.T) { }, }, }) - m.ClusterConfig(device1, protocol.ClusterConfig{}) + m.ClusterConfig(device1Conn, protocol.ClusterConfig{}) if _, ok := m.cfg.Device(device2); ok { t.Error("device 2 should have been removed") @@ -686,7 +686,7 @@ func TestIntroducer(t *testing.T) { }, }, }) - m.ClusterConfig(device1, protocol.ClusterConfig{}) + m.ClusterConfig(device1Conn, protocol.ClusterConfig{}) if _, ok := m.cfg.Device(device2); !ok { t.Error("device 2 should not have been removed") @@ -743,7 +743,7 @@ func TestIntroducer(t *testing.T) { Introducer: true, SkipIntroductionRemovals: true, }) - m.ClusterConfig(device1, cc) + m.ClusterConfig(device1Conn, cc) if _, ok := m.cfg.Device(device2); !ok { t.Error("device 2 should not have been removed") @@ -794,7 +794,7 @@ func TestIntroducer(t *testing.T) { }, }, }) - m.ClusterConfig(device1, protocol.ClusterConfig{}) + m.ClusterConfig(device1Conn, protocol.ClusterConfig{}) if _, ok := m.cfg.Device(device2); !ok { t.Error("device 2 should not have been removed") @@ -847,7 +847,7 @@ func TestIntroducer(t *testing.T) { }) defer cleanupModel(m) defer cancel() - m.ClusterConfig(device1, protocol.ClusterConfig{}) + m.ClusterConfig(device1Conn, protocol.ClusterConfig{}) if _, ok := m.cfg.Device(device2); !ok { t.Error("device 2 should not have been removed") @@ -906,14 +906,14 @@ func TestIssue5063(t *testing.T) { for _, c := range m.conns { conn := c.(*fakeConnection) conn.CloseCalls(func(_ error) {}) - defer m.Closed(c.ConnectionID(), errStopped) // to unblock deferred m.Stop() + defer m.Closed(c, errStopped) // to unblock deferred m.Stop() } m.pmut.Unlock() wg := sync.WaitGroup{} addAndVerify := func(id string) { - m.ClusterConfig(device1, createClusterConfig(device1, id)) + m.ClusterConfig(device1Conn, createClusterConfig(device1, id)) if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) { t.Error("expected shared", id) } @@ -951,7 +951,7 @@ func TestAutoAcceptRejected(t *testing.T) { // defer cleanupModel(m) defer cancel() id := srand.String(8) - m.ClusterConfig(device1, createClusterConfig(device1, id)) + m.ClusterConfig(device1Conn, createClusterConfig(device1, id)) if cfg, ok := m.cfg.Folder(id); ok && cfg.SharedWith(device1) { t.Error("unexpected shared", id) @@ -964,7 +964,7 @@ func TestAutoAcceptNewFolder(t *testing.T) { defer cleanupModel(m) defer cancel() id := srand.String(8) - m.ClusterConfig(device1, createClusterConfig(device1, id)) + m.ClusterConfig(device1Conn, createClusterConfig(device1, id)) if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) { t.Error("expected shared", id) } @@ -976,14 +976,14 @@ func TestAutoAcceptNewFolderFromTwoDevices(t *testing.T) { defer cancel() id := srand.String(8) defer os.RemoveAll(id) - m.ClusterConfig(device1, createClusterConfig(device1, id)) + m.ClusterConfig(device1Conn, createClusterConfig(device1, id)) if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) { t.Error("expected shared", id) } if fcfg, ok := m.cfg.Folder(id); !ok || fcfg.SharedWith(device2) { t.Error("unexpected expected shared", id) } - m.ClusterConfig(device2, createClusterConfig(device2, id)) + m.ClusterConfig(device2Conn, createClusterConfig(device2, id)) if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device2) { t.Error("expected shared", id) } @@ -997,14 +997,14 @@ func TestAutoAcceptNewFolderFromOnlyOneDevice(t *testing.T) { defer os.RemoveAll(id) defer cleanupModel(m) defer cancel() - m.ClusterConfig(device1, createClusterConfig(device1, id)) + m.ClusterConfig(device1Conn, createClusterConfig(device1, id)) if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) { t.Error("expected shared", id) } if fcfg, ok := m.cfg.Folder(id); !ok || fcfg.SharedWith(device2) { t.Error("unexpected expected shared", id) } - m.ClusterConfig(device2, createClusterConfig(device2, id)) + m.ClusterConfig(device2Conn, createClusterConfig(device2, id)) if fcfg, ok := m.cfg.Folder(id); !ok || fcfg.SharedWith(device2) { t.Error("unexpected shared", id) } @@ -1035,10 +1035,10 @@ func TestAutoAcceptNewFolderPremutationsNoPanic(t *testing.T) { cfg.Folders = append(cfg.Folders, fcfg) } m, cancel := newState(t, cfg) - m.ClusterConfig(device1, protocol.ClusterConfig{ + m.ClusterConfig(device1Conn, protocol.ClusterConfig{ Folders: []protocol.Folder{dev1folder}, }) - m.ClusterConfig(device2, protocol.ClusterConfig{ + m.ClusterConfig(device2Conn, protocol.ClusterConfig{ Folders: []protocol.Folder{dev2folder}, }) cleanupModel(m) @@ -1058,7 +1058,7 @@ func TestAutoAcceptMultipleFolders(t *testing.T) { m, cancel := newState(t, defaultAutoAcceptCfg) defer cleanupModel(m) defer cancel() - m.ClusterConfig(device1, createClusterConfig(device1, id1, id2)) + m.ClusterConfig(device1Conn, createClusterConfig(device1, id1, id2)) if fcfg, ok := m.cfg.Folder(id1); !ok || !fcfg.SharedWith(device1) { t.Error("expected shared", id1) } @@ -1086,7 +1086,7 @@ func TestAutoAcceptExistingFolder(t *testing.T) { if fcfg, ok := m.cfg.Folder(id); !ok || fcfg.SharedWith(device1) { t.Error("missing folder, or shared", id) } - m.ClusterConfig(device1, createClusterConfig(device1, id)) + m.ClusterConfig(device1Conn, createClusterConfig(device1, id)) if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) || fcfg.Path != idOther { t.Error("missing folder, or unshared, or path changed", id) @@ -1112,7 +1112,7 @@ func TestAutoAcceptNewAndExistingFolder(t *testing.T) { if fcfg, ok := m.cfg.Folder(id1); !ok || fcfg.SharedWith(device1) { t.Error("missing folder, or shared", id1) } - m.ClusterConfig(device1, createClusterConfig(device1, id1, id2)) + m.ClusterConfig(device1Conn, createClusterConfig(device1, id1, id2)) for i, id := range []string{id1, id2} { if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) { @@ -1143,7 +1143,7 @@ func TestAutoAcceptAlreadyShared(t *testing.T) { if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) { t.Error("missing folder, or not shared", id) } - m.ClusterConfig(device1, createClusterConfig(device1, id)) + m.ClusterConfig(device1Conn, createClusterConfig(device1, id)) if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) { t.Error("missing folder, or not shared", id) @@ -1159,7 +1159,7 @@ func TestAutoAcceptNameConflict(t *testing.T) { m, cancel := newState(t, defaultAutoAcceptCfg) defer cleanupModel(m) defer cancel() - m.ClusterConfig(device1, protocol.ClusterConfig{ + m.ClusterConfig(device1Conn, protocol.ClusterConfig{ Folders: []protocol.Folder{ { ID: id, @@ -1179,7 +1179,7 @@ func TestAutoAcceptPrefersLabel(t *testing.T) { label := srand.String(8) defer cleanupModel(m) defer cancel() - m.ClusterConfig(device1, addFolderDevicesToClusterConfig(protocol.ClusterConfig{ + m.ClusterConfig(device1Conn, addFolderDevicesToClusterConfig(protocol.ClusterConfig{ Folders: []protocol.Folder{ { ID: id, @@ -1203,7 +1203,7 @@ func TestAutoAcceptFallsBackToID(t *testing.T) { } defer cleanupModel(m) defer cancel() - m.ClusterConfig(device1, addFolderDevicesToClusterConfig(protocol.ClusterConfig{ + m.ClusterConfig(device1Conn, addFolderDevicesToClusterConfig(protocol.ClusterConfig{ Folders: []protocol.Folder{ { ID: id, @@ -1245,7 +1245,7 @@ func TestAutoAcceptPausedWhenFolderConfigChanged(t *testing.T) { t.Fatal("folder running?") } - m.ClusterConfig(device1, createClusterConfig(device1, id)) + m.ClusterConfig(device1Conn, createClusterConfig(device1, id)) m.generateClusterConfig(device1) if fcfg, ok := m.cfg.Folder(id); !ok { @@ -1294,7 +1294,7 @@ func TestAutoAcceptPausedWhenFolderConfigNotChanged(t *testing.T) { t.Fatal("folder running?") } - m.ClusterConfig(device1, createClusterConfig(device1, id)) + m.ClusterConfig(device1Conn, createClusterConfig(device1, id)) m.generateClusterConfig(device1) if fcfg, ok := m.cfg.Folder(id); !ok { @@ -1338,7 +1338,7 @@ func TestAutoAcceptEnc(t *testing.T) { // would panic. clusterConfig := func(deviceID protocol.DeviceID, cm protocol.ClusterConfig) { m.AddConnection(newFakeConnection(deviceID, m), protocol.Hello{}) - m.ClusterConfig(deviceID, cm) + m.ClusterConfig(&protocolmocks.Connection{IDStub: func() protocol.DeviceID { return deviceID }}, cm) } clusterConfig(device1, basicCC()) @@ -1806,7 +1806,7 @@ func TestGlobalDirectoryTree(t *testing.T) { return string(bytes) } - must(t, m.Index(device1, "default", testdata)) + must(t, m.Index(device1Conn, "default", testdata)) result, _ := m.GlobalDirectoryTree("default", "", -1, false) @@ -2013,7 +2013,7 @@ func benchmarkTree(b *testing.B, n1, n2 int) { m.ScanFolder(fcfg.ID) files := genDeepFiles(n1, n2) - must(b, m.Index(device1, fcfg.ID, files)) + must(b, m.Index(device1Conn, fcfg.ID, files)) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -2159,7 +2159,7 @@ func TestSharedWithClearedOnDisconnect(t *testing.T) { conn2 := newFakeConnection(device2, m) m.AddConnection(conn2, protocol.Hello{}) - m.ClusterConfig(device1, protocol.ClusterConfig{ + m.ClusterConfig(device1Conn, protocol.ClusterConfig{ Folders: []protocol.Folder{ { ID: "default", @@ -2171,7 +2171,7 @@ func TestSharedWithClearedOnDisconnect(t *testing.T) { }, }, }) - m.ClusterConfig(device2, protocol.ClusterConfig{ + m.ClusterConfig(device2Conn, protocol.ClusterConfig{ Folders: []protocol.Folder{ { ID: "default", @@ -2424,7 +2424,7 @@ func TestRemoveDirWithContent(t *testing.T) { file.Deleted = true file.Version = file.Version.Update(device1.Short()).Update(device1.Short()) - must(t, m.IndexUpdate(device1, fcfg.ID, []protocol.FileInfo{dir, file})) + must(t, m.IndexUpdate(device1Conn, fcfg.ID, []protocol.FileInfo{dir, file})) // Is there something we could trigger on instead of just waiting? timeout := time.NewTimer(5 * time.Second) @@ -2919,14 +2919,14 @@ func TestRequestLimit(t *testing.T) { m.ScanFolder("default") befReq := time.Now() - first, err := m.Request(device1, "default", file, 0, 2000, 0, nil, 0, false) + first, err := m.Request(device1Conn, "default", file, 0, 2000, 0, nil, 0, false) if err != nil { t.Fatalf("First request failed: %v", err) } reqDur := time.Since(befReq) returned := make(chan struct{}) go func() { - second, err := m.Request(device1, "default", file, 0, 2000, 0, nil, 0, false) + second, err := m.Request(device1Conn, "default", file, 0, 2000, 0, nil, 0, false) if err != nil { t.Errorf("Second request failed: %v", err) } @@ -2964,13 +2964,15 @@ func TestConnCloseOnRestart(t *testing.T) { br := &testutils.BlockingRW{} nw := &testutils.NoopRW{} - conn := protocol.NewConnection(device1, br, nw, testutils.NoopCloser{}, m, new(protocolmocks.ConnectionInfo), protocol.CompressionNever, nil, m.keyGen) - m.AddConnection(conn, protocol.Hello{}) + m.AddConnection(protocol.NewConnection(device1, br, nw, testutils.NoopCloser{}, m, new(protocolmocks.ConnectionInfo), protocol.CompressionNever, nil, m.keyGen), protocol.Hello{}) m.pmut.RLock() if len(m.closed) != 1 { - t.Fatalf("Expected just one conn (len(m.conns) == %v)", len(m.conns)) + t.Fatalf("Expected just one conn (len(m.closed) == %v)", len(m.closed)) + } + var closed chan struct{} + for _, c := range m.closed { + closed = c } - closed := m.closed[conn.ConnectionID()] m.pmut.RUnlock() waiter, err := w.RemoveDevice(device1) @@ -3065,7 +3067,10 @@ func TestDevicePause(t *testing.T) { defer sub.Unsubscribe() m.pmut.RLock() - closed := m.closed[m.deviceConns[device1][0]] + var closed chan struct{} + for _, c := range m.closed { + closed = c + } m.pmut.RUnlock() pauseDevice(t, m.cfg, device1, true) @@ -3581,7 +3586,7 @@ func TestScanDeletedROChangedOnSR(t *testing.T) { } // A remote must have the file, otherwise the deletion below is // automatically resolved as not a ro-changed item. - must(t, m.IndexUpdate(device1, fcfg.ID, []protocol.FileInfo{file})) + must(t, m.IndexUpdate(device1Conn, fcfg.ID, []protocol.FileInfo{file})) must(t, ffs.Remove(name)) m.ScanFolders() @@ -3694,9 +3699,9 @@ func TestIssue6961(t *testing.T) { version := protocol.Vector{}.Update(device1.Short()) // Remote, valid and existing file - must(t, m.Index(device1, fcfg.ID, []protocol.FileInfo{{Name: name, Version: version, Sequence: 1}})) + must(t, m.Index(device1Conn, fcfg.ID, []protocol.FileInfo{{Name: name, Version: version, Sequence: 1}})) // Remote, invalid (receive-only) and existing file - must(t, m.Index(device2, fcfg.ID, []protocol.FileInfo{{Name: name, RawInvalid: true, Sequence: 1}})) + must(t, m.Index(device2Conn, fcfg.ID, []protocol.FileInfo{{Name: name, RawInvalid: true, Sequence: 1}})) // Create a local file if fd, err := tfs.OpenFile(name, fs.OptCreate, 0o666); err != nil { t.Fatal(err) @@ -3722,7 +3727,7 @@ func TestIssue6961(t *testing.T) { m.ScanFolders() // Drop the remote index, add some other file. - must(t, m.Index(device2, fcfg.ID, []protocol.FileInfo{{Name: "bar", RawInvalid: true, Sequence: 1}})) + must(t, m.Index(device2Conn, fcfg.ID, []protocol.FileInfo{{Name: "bar", RawInvalid: true, Sequence: 1}})) // Pause and unpause folder to create new db.FileSet and thus recalculate everything pauseFolder(t, wcfg, fcfg.ID, true) @@ -3745,7 +3750,7 @@ func TestCompletionEmptyGlobal(t *testing.T) { m.fmut.Unlock() files[0].Deleted = true files[0].Version = files[0].Version.Update(device1.Short()) - must(t, m.IndexUpdate(device1, fcfg.ID, files)) + must(t, m.IndexUpdate(device1Conn, fcfg.ID, files)) comp := m.testCompletion(protocol.LocalDeviceID, fcfg.ID) if comp.CompletionPct != 95 { t.Error("Expected completion of 95%, got", comp.CompletionPct) @@ -3766,26 +3771,26 @@ func TestNeedMetaAfterIndexReset(t *testing.T) { // Start with two remotes having one file, then both deleting it, then // only one adding it again. - must(t, m.Index(device1, fcfg.ID, files)) - must(t, m.Index(device2, fcfg.ID, files)) + must(t, m.Index(device1Conn, fcfg.ID, files)) + must(t, m.Index(device2Conn, fcfg.ID, files)) seq++ files[0].SetDeleted(device2.Short()) files[0].Sequence = seq - must(t, m.IndexUpdate(device2, fcfg.ID, files)) - must(t, m.IndexUpdate(device1, fcfg.ID, files)) + must(t, m.IndexUpdate(device2Conn, fcfg.ID, files)) + must(t, m.IndexUpdate(device1Conn, fcfg.ID, files)) seq++ files[0].Deleted = false files[0].Size = 20 files[0].Version = files[0].Version.Update(device1.Short()) files[0].Sequence = seq - must(t, m.IndexUpdate(device1, fcfg.ID, files)) + must(t, m.IndexUpdate(device1Conn, fcfg.ID, files)) if comp := m.testCompletion(device2, fcfg.ID); comp.NeedItems != 1 { t.Error("Expected one needed item for device2, got", comp.NeedItems) } // Pretend we had an index reset on device 1 - must(t, m.Index(device1, fcfg.ID, files)) + must(t, m.Index(device1Conn, fcfg.ID, files)) if comp := m.testCompletion(device2, fcfg.ID); comp.NeedItems != 1 { t.Error("Expected one needed item for device2, got", comp.NeedItems) } diff --git a/lib/model/requests_test.go b/lib/model/requests_test.go index 32d146575..6e1f7d0c7 100644 --- a/lib/model/requests_test.go +++ b/lib/model/requests_test.go @@ -107,7 +107,7 @@ func TestSymlinkTraversalRead(t *testing.T) { <-done // Request a file by traversing the symlink - res, err := m.Request(device1, "default", "symlink/requests_test.go", 0, 10, 0, nil, 0, false) + res, err := m.Request(device1Conn, "default", "symlink/requests_test.go", 0, 10, 0, nil, 0, false) if err == nil || res != nil { t.Error("Managed to traverse symlink") } @@ -439,7 +439,7 @@ func TestRescanIfHaveInvalidContent(t *testing.T) { t.Fatalf("unexpected weak hash: %d != 103547413", f.Blocks[0].WeakHash) } - res, err := m.Request(device1, "default", "foo", 0, int32(len(payload)), 0, f.Blocks[0].Hash, f.Blocks[0].WeakHash, false) + res, err := m.Request(device1Conn, "default", "foo", 0, int32(len(payload)), 0, f.Blocks[0].Hash, f.Blocks[0].WeakHash, false) if err != nil { t.Fatal(err) } @@ -453,7 +453,7 @@ func TestRescanIfHaveInvalidContent(t *testing.T) { writeFile(t, tfs, "foo", payload) - _, err = m.Request(device1, "default", "foo", 0, int32(len(payload)), 0, f.Blocks[0].Hash, f.Blocks[0].WeakHash, false) + _, err = m.Request(device1Conn, "default", "foo", 0, int32(len(payload)), 0, f.Blocks[0].Hash, f.Blocks[0].WeakHash, false) if err == nil { t.Fatalf("expected failure") } @@ -1122,7 +1122,7 @@ func TestRequestIndexSenderPause(t *testing.T) { cc := basicClusterConfig(device1, myID, fcfg.ID) cc.Folders[0].Paused = true - m.ClusterConfig(device1, cc) + m.ClusterConfig(device1Conn, cc) seq++ files[0].Sequence = seq @@ -1143,7 +1143,7 @@ func TestRequestIndexSenderPause(t *testing.T) { // Remote unpaused cc.Folders[0].Paused = false - m.ClusterConfig(device1, cc) + m.ClusterConfig(device1Conn, cc) select { case <-time.After(5 * time.Second): t.Fatal("timed out before receiving index") @@ -1168,12 +1168,12 @@ func TestRequestIndexSenderPause(t *testing.T) { // Local and remote paused, then first resume remote, then local cc.Folders[0].Paused = true - m.ClusterConfig(device1, cc) + m.ClusterConfig(device1Conn, cc) pauseFolder(t, m.cfg, fcfg.ID, true) cc.Folders[0].Paused = false - m.ClusterConfig(device1, cc) + m.ClusterConfig(device1Conn, cc) pauseFolder(t, m.cfg, fcfg.ID, false) @@ -1190,7 +1190,7 @@ func TestRequestIndexSenderPause(t *testing.T) { // Folder removed on remote cc = protocol.ClusterConfig{} - m.ClusterConfig(device1, cc) + m.ClusterConfig(device1Conn, cc) seq++ files[0].Sequence = seq @@ -1304,7 +1304,7 @@ func TestRequestReceiveEncrypted(t *testing.T) { return nil }) m.AddConnection(fc, protocol.Hello{}) - m.ClusterConfig(device1, protocol.ClusterConfig{ + m.ClusterConfig(device1Conn, protocol.ClusterConfig{ Folders: []protocol.Folder{ { ID: "default", @@ -1354,7 +1354,7 @@ func TestRequestReceiveEncrypted(t *testing.T) { } // Simulate request from device that is untrusted too, i.e. with non-empty, but garbage hash - _, err := m.Request(device1, fcfg.ID, name, 0, 1064, 0, []byte("garbage"), 0, false) + _, err := m.Request(device1Conn, fcfg.ID, name, 0, 1064, 0, []byte("garbage"), 0, false) must(t, err) changed, err := m.LocalChangedFolderFiles(fcfg.ID, 1, 10) @@ -1405,7 +1405,7 @@ func TestRequestGlobalInvalidToValid(t *testing.T) { file := fc.files[0] fc.mut.Unlock() file.SetIgnored() - m.IndexUpdate(device2, fcfg.ID, []protocol.FileInfo{prepareFileInfoForIndex(file)}) + m.IndexUpdate(device2Conn, fcfg.ID, []protocol.FileInfo{prepareFileInfoForIndex(file)}) // Wait for the ignored file to be received and possible pulled timeout := time.After(10 * time.Second) diff --git a/lib/model/testutils_test.go b/lib/model/testutils_test.go index 58bc06233..25c5a001f 100644 --- a/lib/model/testutils_test.go +++ b/lib/model/testutils_test.go @@ -19,6 +19,7 @@ import ( "github.com/syncthing/syncthing/lib/fs" "github.com/syncthing/syncthing/lib/ignore" "github.com/syncthing/syncthing/lib/protocol" + "github.com/syncthing/syncthing/lib/protocol/mocks" "github.com/syncthing/syncthing/lib/rand" ) @@ -29,6 +30,16 @@ var ( defaultFolderConfig config.FolderConfiguration defaultCfg config.Configuration defaultAutoAcceptCfg config.Configuration + device1Conn = &mocks.Connection{ + IDStub: func() protocol.DeviceID { + return device1 + }, + } + device2Conn = &mocks.Connection{ + IDStub: func() protocol.DeviceID { + return device2 + }, + } ) func init() { diff --git a/lib/protocol/benchmark_test.go b/lib/protocol/benchmark_test.go index 7c2b9fb4b..4fd07c407 100644 --- a/lib/protocol/benchmark_test.go +++ b/lib/protocol/benchmark_test.go @@ -167,15 +167,15 @@ func negotiateTLS(cert tls.Certificate, conn0, conn1 net.Conn) (net.Conn, net.Co type fakeModel struct{} -func (*fakeModel) Index(_ DeviceID, _ string, _ []FileInfo) error { +func (*fakeModel) Index(Connection, string, []FileInfo) error { return nil } -func (*fakeModel) IndexUpdate(_ DeviceID, _ string, _ []FileInfo) error { +func (*fakeModel) IndexUpdate(Connection, string, []FileInfo) error { return nil } -func (*fakeModel) Request(_ DeviceID, _, _ string, _, size int32, offset int64, _ []byte, _ uint32, _ bool) (RequestResponse, error) { +func (*fakeModel) Request(_ Connection, _, _ string, _, size int32, offset int64, _ []byte, _ uint32, _ bool) (RequestResponse, error) { // We write the offset to the end of the buffer, so the receiver // can verify that it did in fact get some data back over the // connection. @@ -184,13 +184,13 @@ func (*fakeModel) Request(_ DeviceID, _, _ string, _, size int32, offset int64, return &fakeRequestResponse{buf}, nil } -func (*fakeModel) ClusterConfig(_ DeviceID, _ ClusterConfig) error { +func (*fakeModel) ClusterConfig(Connection, ClusterConfig) error { return nil } -func (*fakeModel) Closed(string, error) { +func (*fakeModel) Closed(Connection, error) { } -func (*fakeModel) DownloadProgress(_ DeviceID, _ string, _ []FileDownloadProgressUpdate) error { +func (*fakeModel) DownloadProgress(Connection, string, []FileDownloadProgressUpdate) error { return nil } diff --git a/lib/protocol/bep.pb.go b/lib/protocol/bep.pb.go index 38a258a71..77c195dca 100644 --- a/lib/protocol/bep.pb.go +++ b/lib/protocol/bep.pb.go @@ -288,7 +288,8 @@ func (m *Header) XXX_DiscardUnknown() { var xxx_messageInfo_Header proto.InternalMessageInfo type ClusterConfig struct { - Folders []Folder `protobuf:"bytes,1,rep,name=folders,proto3" json:"folders" xml:"folder"` + Folders []Folder `protobuf:"bytes,1,rep,name=folders,proto3" json:"folders" xml:"folder"` + Secondary bool `protobuf:"varint,2,opt,name=secondary,proto3" json:"secondary" xml:"secondary"` } func (m *ClusterConfig) Reset() { *m = ClusterConfig{} } @@ -1142,205 +1143,206 @@ func init() { func init() { proto.RegisterFile("lib/protocol/bep.proto", fileDescriptor_311ef540e10d9705) } var fileDescriptor_311ef540e10d9705 = []byte{ - // 3163 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x5a, 0x4d, 0x6c, 0x1b, 0xc7, - 0xbd, 0x17, 0xc5, 0x0f, 0x51, 0x23, 0xc9, 0xa6, 0xc6, 0x5f, 0x0c, 0x6d, 0x6b, 0xf9, 0x26, 0xce, - 0x7b, 0x8a, 0xf2, 0x62, 0x27, 0xca, 0xc7, 0xcb, 0x8b, 0xf3, 0x1c, 0x88, 0x22, 0x25, 0x33, 0x96, - 0x49, 0x65, 0x28, 0xdb, 0xb1, 0xf1, 0x1e, 0x88, 0x15, 0x77, 0x44, 0x2d, 0x4c, 0xee, 0xf2, 0xed, - 0x52, 0x5f, 0x41, 0x2f, 0x6d, 0x80, 0x20, 0xd0, 0xa1, 0x28, 0x72, 0x2a, 0x8a, 0x0a, 0x0d, 0x7a, - 0xe9, 0xad, 0x40, 0x0f, 0xbd, 0xe4, 0xd4, 0xa3, 0x8f, 0x46, 0x80, 0x02, 0x45, 0x0f, 0x0b, 0xc4, - 0xbe, 0xb4, 0xec, 0x8d, 0xc7, 0x9e, 0x8a, 0xf9, 0xcf, 0xec, 0xec, 0xac, 0x3e, 0x52, 0x39, 0x39, - 0xf4, 0x64, 0xfe, 0x7f, 0xff, 0xdf, 0xff, 0xbf, 0xb3, 0xf3, 0xff, 0x9a, 0x59, 0x19, 0x5d, 0xec, - 0xd8, 0xeb, 0x37, 0x7a, 0x9e, 0xdb, 0x77, 0x5b, 0x6e, 0xe7, 0xc6, 0x3a, 0xeb, 0x5d, 0x07, 0x01, - 0x67, 0x43, 0xac, 0x30, 0xce, 0x76, 0xfb, 0x02, 0x2c, 0xbc, 0xec, 0xb1, 0x9e, 0xeb, 0x0b, 0xfa, - 0xfa, 0xd6, 0xc6, 0x8d, 0xb6, 0xdb, 0x76, 0x41, 0x80, 0x5f, 0x82, 0x44, 0x9e, 0x25, 0x50, 0xfa, - 0x36, 0xeb, 0x74, 0x5c, 0xbc, 0x88, 0x26, 0x2c, 0xb6, 0x6d, 0xb7, 0x58, 0xd3, 0x31, 0xbb, 0x2c, - 0x9f, 0x28, 0x26, 0x66, 0xc7, 0x4b, 0x64, 0x10, 0x18, 0x48, 0xc0, 0x35, 0xb3, 0xcb, 0x86, 0x81, - 0x91, 0xdb, 0xed, 0x76, 0xde, 0x27, 0x11, 0x44, 0xa8, 0xa6, 0xe7, 0x4e, 0x5a, 0x1d, 0x9b, 0x39, - 0x7d, 0xe1, 0x64, 0x34, 0x72, 0x22, 0xe0, 0x98, 0x93, 0x08, 0x22, 0x54, 0xd3, 0xe3, 0x3a, 0x3a, - 0x23, 0x9d, 0x6c, 0x33, 0xcf, 0xb7, 0x5d, 0x27, 0x9f, 0x04, 0x3f, 0xb3, 0x83, 0xc0, 0x98, 0x12, - 0x9a, 0xfb, 0x42, 0x31, 0x0c, 0x8c, 0x73, 0x9a, 0x2b, 0x89, 0x12, 0x1a, 0x67, 0x91, 0xdf, 0x25, - 0x50, 0xe6, 0x36, 0x33, 0x2d, 0xe6, 0xe1, 0x05, 0x94, 0xea, 0xef, 0xf5, 0xc4, 0xeb, 0x9d, 0x99, - 0xbf, 0x70, 0x3d, 0xdc, 0xb8, 0xeb, 0x77, 0x99, 0xef, 0x9b, 0x6d, 0xb6, 0xb6, 0xd7, 0x63, 0xa5, - 0x8b, 0x83, 0xc0, 0x00, 0xda, 0x30, 0x30, 0x10, 0xf8, 0xe7, 0x02, 0xa1, 0x80, 0x61, 0x0b, 0x4d, - 0xb4, 0xdc, 0x6e, 0xcf, 0x63, 0x3e, 0xac, 0x6d, 0x14, 0x3c, 0x5d, 0x39, 0xe2, 0x69, 0x31, 0xe2, - 0x94, 0xae, 0x0d, 0x02, 0x43, 0x37, 0x1a, 0x06, 0xc6, 0xb4, 0x58, 0x77, 0x84, 0x11, 0xaa, 0x33, - 0xc8, 0xff, 0xa2, 0xa9, 0xc5, 0xce, 0x96, 0xdf, 0x67, 0xde, 0xa2, 0xeb, 0x6c, 0xd8, 0x6d, 0x7c, - 0x07, 0x8d, 0x6d, 0xb8, 0x1d, 0x8b, 0x79, 0x7e, 0x3e, 0x51, 0x4c, 0xce, 0x4e, 0xcc, 0xe7, 0xa2, - 0x47, 0x2e, 0x81, 0xa2, 0x64, 0x3c, 0x09, 0x8c, 0x91, 0x41, 0x60, 0x84, 0xc4, 0x61, 0x60, 0x4c, - 0xc2, 0x63, 0x84, 0x4c, 0x68, 0xa8, 0x20, 0x5f, 0xa7, 0x50, 0x46, 0x18, 0xe1, 0xeb, 0x68, 0xd4, - 0xb6, 0x64, 0xb8, 0x67, 0x9e, 0x05, 0xc6, 0x68, 0xb5, 0x3c, 0x08, 0x8c, 0x51, 0xdb, 0x1a, 0x06, - 0x46, 0x16, 0xac, 0x6d, 0x8b, 0x7c, 0xf9, 0xf4, 0xda, 0x68, 0xb5, 0x4c, 0x47, 0x6d, 0x0b, 0x5f, - 0x47, 0xe9, 0x8e, 0xb9, 0xce, 0x3a, 0x32, 0xb8, 0xf9, 0x41, 0x60, 0x08, 0x60, 0x18, 0x18, 0x13, - 0xc0, 0x07, 0x89, 0x50, 0x81, 0xe2, 0x9b, 0x68, 0xdc, 0x63, 0xa6, 0xd5, 0x74, 0x9d, 0xce, 0x1e, - 0x04, 0x32, 0x5b, 0x9a, 0x19, 0x04, 0x46, 0x96, 0x83, 0x75, 0xa7, 0xb3, 0x37, 0x0c, 0x8c, 0x33, - 0x60, 0x16, 0x02, 0x84, 0x2a, 0x1d, 0x6e, 0x22, 0x6c, 0xb7, 0x1d, 0xd7, 0x63, 0xcd, 0x1e, 0xf3, - 0xba, 0x36, 0x6c, 0x8d, 0x9f, 0x4f, 0x81, 0x97, 0x37, 0x06, 0x81, 0x31, 0x2d, 0xb4, 0xab, 0x91, - 0x72, 0x18, 0x18, 0x97, 0xc4, 0xaa, 0x0f, 0x6b, 0x08, 0x3d, 0xca, 0xc6, 0x77, 0xd0, 0x94, 0x7c, - 0x80, 0xc5, 0x3a, 0xac, 0xcf, 0xf2, 0x69, 0xf0, 0xfd, 0xef, 0x83, 0xc0, 0x98, 0x14, 0x8a, 0x32, - 0xe0, 0xc3, 0xc0, 0xc0, 0x9a, 0x5b, 0x01, 0x12, 0x1a, 0xe3, 0x60, 0x0b, 0x9d, 0xb7, 0x6c, 0xdf, - 0x5c, 0xef, 0xb0, 0x66, 0x9f, 0x75, 0x7b, 0x4d, 0xdb, 0xb1, 0xd8, 0x2e, 0xf3, 0xf3, 0x19, 0xf0, - 0x39, 0x3f, 0x08, 0x0c, 0x2c, 0xf5, 0x6b, 0xac, 0xdb, 0xab, 0x0a, 0xed, 0x30, 0x30, 0xf2, 0xa2, - 0xa6, 0x8e, 0xa8, 0x08, 0x3d, 0x86, 0x8f, 0xe7, 0x51, 0xa6, 0x67, 0x6e, 0xf9, 0xcc, 0xca, 0x8f, - 0x81, 0xdf, 0xc2, 0x20, 0x30, 0x24, 0xa2, 0x02, 0x2e, 0x44, 0x42, 0x25, 0xce, 0x93, 0x47, 0x54, - 0xa9, 0x9f, 0xcf, 0x1d, 0x4e, 0x9e, 0x32, 0x28, 0xa2, 0xe4, 0x91, 0x44, 0xe5, 0x4b, 0xc8, 0x84, - 0x86, 0x0a, 0xf2, 0x87, 0x0c, 0xca, 0x08, 0x23, 0x5c, 0x52, 0xc9, 0x33, 0x59, 0x9a, 0xe7, 0x0e, - 0xfe, 0x1c, 0x18, 0x59, 0xa1, 0xab, 0x96, 0x4f, 0x4a, 0xa6, 0x2f, 0x9e, 0x5e, 0x4b, 0x68, 0x09, - 0x35, 0x87, 0x52, 0x5a, 0xb3, 0x80, 0xda, 0x73, 0x44, 0x9b, 0x10, 0xb5, 0xe7, 0x40, 0x83, 0x00, - 0x0c, 0x7f, 0x80, 0xc6, 0x4d, 0xcb, 0xe2, 0x35, 0xc2, 0xfc, 0x7c, 0xb2, 0x98, 0xe4, 0x39, 0x3b, - 0x08, 0x8c, 0x08, 0x1c, 0x06, 0xc6, 0x14, 0x58, 0x49, 0x84, 0xd0, 0x48, 0x87, 0xff, 0x2f, 0x5e, - 0xb9, 0xa9, 0xc3, 0x3d, 0xe0, 0x87, 0x95, 0x2c, 0xcf, 0xf4, 0x16, 0xf3, 0x64, 0xeb, 0x4b, 0x8b, - 0x82, 0xe2, 0x99, 0xce, 0x41, 0xd9, 0xf8, 0x44, 0xa6, 0x87, 0x00, 0xa1, 0x4a, 0x87, 0x97, 0xd1, - 0x64, 0xd7, 0xdc, 0x6d, 0xfa, 0xec, 0xff, 0xb7, 0x98, 0xd3, 0x62, 0x90, 0x33, 0x49, 0xb1, 0x8a, - 0xae, 0xb9, 0xdb, 0x90, 0xb0, 0x5a, 0x85, 0x86, 0x11, 0xaa, 0x33, 0x70, 0x09, 0x21, 0xdb, 0xe9, - 0x7b, 0xae, 0xb5, 0xd5, 0x62, 0x9e, 0x4c, 0x11, 0xe8, 0xc0, 0x11, 0xaa, 0x3a, 0x70, 0x04, 0x11, - 0xaa, 0xe9, 0x71, 0x1b, 0x65, 0x21, 0x77, 0x9b, 0xb6, 0x95, 0xcf, 0x16, 0x13, 0xb3, 0xa9, 0xd2, - 0x8a, 0x0c, 0xee, 0x18, 0x64, 0x21, 0xc4, 0x36, 0xfc, 0xc9, 0x73, 0x06, 0xd8, 0x55, 0x4b, 0xed, - 0xbe, 0x94, 0x79, 0xdf, 0x08, 0x69, 0xbf, 0x88, 0x7e, 0xd2, 0x90, 0x8f, 0x7f, 0x84, 0x0a, 0xfe, - 0x63, 0x9b, 0x57, 0x8a, 0x78, 0x76, 0xdf, 0x76, 0x9d, 0xa6, 0xc7, 0xba, 0xee, 0xb6, 0xd9, 0xf1, - 0xf3, 0xe3, 0xb0, 0xf8, 0x5b, 0x83, 0xc0, 0xc8, 0x73, 0x56, 0x55, 0x23, 0x51, 0xc9, 0x19, 0x06, - 0xc6, 0x0c, 0x3c, 0xf1, 0x24, 0x02, 0xa1, 0x27, 0xda, 0xe2, 0x5d, 0xf4, 0x12, 0x73, 0x5a, 0xde, - 0x5e, 0x0f, 0x1e, 0xdb, 0x33, 0x7d, 0x7f, 0xc7, 0xf5, 0xac, 0x66, 0xdf, 0x7d, 0xcc, 0x9c, 0x3c, - 0x82, 0xa4, 0xfe, 0x60, 0x10, 0x18, 0x97, 0x22, 0xd2, 0xaa, 0xe4, 0xac, 0x71, 0xca, 0x30, 0x30, - 0xae, 0xc2, 0xb3, 0x4f, 0xd0, 0x13, 0x7a, 0x92, 0x25, 0xf9, 0x49, 0x02, 0xa5, 0x61, 0x33, 0x78, - 0x35, 0x8b, 0xa6, 0x2c, 0x5b, 0x30, 0x54, 0xb3, 0x40, 0x8e, 0xb4, 0x6f, 0x89, 0xe3, 0x0a, 0x4a, - 0x6f, 0xd8, 0x1d, 0xe6, 0xe7, 0x47, 0xa1, 0x96, 0xb1, 0x36, 0x08, 0xec, 0x0e, 0xab, 0x3a, 0x1b, - 0x6e, 0xe9, 0xb2, 0xac, 0x66, 0x41, 0x54, 0xb5, 0xc4, 0x25, 0x42, 0x05, 0x48, 0xbe, 0x48, 0xa0, - 0x09, 0x58, 0xc4, 0xbd, 0x9e, 0x65, 0xf6, 0xd9, 0xbf, 0x72, 0x29, 0x9f, 0x4f, 0xa1, 0x6c, 0x68, - 0xa0, 0x1a, 0x42, 0xe2, 0x14, 0x0d, 0x61, 0x0e, 0xa5, 0x7c, 0xfb, 0x53, 0x06, 0x83, 0x25, 0x29, - 0xb8, 0x5c, 0x56, 0x5c, 0x2e, 0x10, 0x0a, 0x18, 0xfe, 0x10, 0xa1, 0xae, 0x6b, 0xd9, 0x1b, 0x36, - 0xb3, 0x9a, 0x3e, 0x14, 0x68, 0xb2, 0x54, 0xe4, 0xdd, 0x23, 0x44, 0x1b, 0xc3, 0xc0, 0x38, 0x2b, - 0xca, 0x2b, 0x44, 0x08, 0x8d, 0xb4, 0xbc, 0x7f, 0x28, 0x07, 0xeb, 0x7b, 0xf9, 0x49, 0xa8, 0x8c, - 0x0f, 0xc2, 0xca, 0x68, 0x6c, 0xba, 0x5e, 0x1f, 0xca, 0x41, 0x3d, 0xa6, 0xb4, 0xa7, 0x4a, 0x2d, - 0x82, 0x08, 0xaf, 0x04, 0x49, 0xa6, 0x1a, 0x15, 0xaf, 0xa0, 0xb1, 0xf0, 0xc0, 0xc3, 0x33, 0x3f, - 0xd6, 0xa4, 0xef, 0xb3, 0x56, 0xdf, 0xf5, 0x4a, 0xc5, 0xb0, 0x49, 0x6f, 0xab, 0x03, 0x90, 0x28, - 0xb8, 0xed, 0xf0, 0xe8, 0x13, 0x6a, 0xf0, 0xfb, 0x28, 0xab, 0x9a, 0x09, 0x82, 0x77, 0x85, 0x66, - 0xe4, 0x47, 0x9d, 0x44, 0x34, 0x23, 0x5f, 0xb5, 0x11, 0xa5, 0xc3, 0x1f, 0xa1, 0xcc, 0x7a, 0xc7, - 0x6d, 0x3d, 0x0e, 0xa7, 0xc5, 0xb9, 0x68, 0x21, 0x25, 0x8e, 0x43, 0x5c, 0xaf, 0xca, 0xb5, 0x48, - 0xaa, 0x1a, 0xff, 0x20, 0x12, 0x2a, 0x61, 0x7e, 0x9a, 0xf3, 0xf7, 0xba, 0x1d, 0xdb, 0x79, 0xdc, - 0xec, 0x9b, 0x5e, 0x9b, 0xf5, 0xf3, 0xd3, 0xd1, 0x69, 0x4e, 0x6a, 0xd6, 0x40, 0xa1, 0x4e, 0x73, - 0x31, 0x94, 0xd0, 0x38, 0x8b, 0x9f, 0x31, 0x85, 0xeb, 0xe6, 0xa6, 0xe9, 0x6f, 0xe6, 0x31, 0xd4, - 0x29, 0x74, 0x38, 0x01, 0xdf, 0x36, 0xfd, 0x4d, 0xb5, 0xed, 0x11, 0x44, 0xa8, 0xa6, 0xc7, 0xb7, - 0xd0, 0xb8, 0xac, 0x4d, 0x66, 0xe5, 0xcf, 0x81, 0x0b, 0x48, 0x05, 0x05, 0xaa, 0x54, 0x50, 0x08, - 0xa1, 0x91, 0x16, 0x97, 0xe4, 0x39, 0x52, 0x9c, 0xfe, 0x2e, 0x1e, 0x4d, 0xfb, 0x53, 0x1c, 0x24, - 0x97, 0xd0, 0xc4, 0xe1, 0x53, 0xcd, 0x94, 0xe8, 0xf8, 0xbd, 0xd8, 0x79, 0x46, 0x74, 0xfc, 0x9e, - 0x7e, 0x92, 0xd1, 0x19, 0xf8, 0x23, 0x2d, 0x2d, 0x1d, 0x3f, 0x3f, 0x51, 0x4c, 0xcc, 0xa6, 0x4b, - 0xaf, 0xea, 0x79, 0x58, 0xf3, 0x8f, 0xe4, 0x61, 0xcd, 0x27, 0x7f, 0x0f, 0x8c, 0xa4, 0xed, 0xf4, - 0xa9, 0x46, 0xc3, 0x1b, 0x48, 0xec, 0x52, 0x13, 0xaa, 0x6a, 0x0a, 0x5c, 0x2d, 0x3f, 0x0b, 0x8c, - 0x49, 0x6a, 0xee, 0x40, 0xe8, 0x1b, 0xf6, 0xa7, 0x8c, 0x6f, 0xd4, 0x7a, 0x28, 0xa8, 0x8d, 0x52, - 0x48, 0xe8, 0xf8, 0xcb, 0xa7, 0xd7, 0x62, 0x66, 0x34, 0x32, 0xc2, 0xf7, 0x51, 0xb6, 0xd7, 0x31, - 0xfb, 0x1b, 0xae, 0xd7, 0xcd, 0x9f, 0x81, 0x64, 0xd7, 0xf6, 0x70, 0x55, 0x6a, 0xca, 0x66, 0xdf, - 0x2c, 0x11, 0x99, 0x66, 0x8a, 0xaf, 0x32, 0x37, 0x04, 0x08, 0x55, 0x3a, 0x5c, 0x46, 0x13, 0x1d, - 0xb7, 0x65, 0x76, 0x9a, 0x1b, 0x1d, 0xb3, 0xed, 0xe7, 0xff, 0x32, 0x06, 0x9b, 0x0a, 0xd9, 0x01, - 0xf8, 0x12, 0x87, 0xd5, 0x66, 0x44, 0x10, 0xa1, 0x9a, 0x1e, 0xdf, 0x46, 0x93, 0xb2, 0x8c, 0x44, - 0x8e, 0xfd, 0x75, 0x0c, 0x32, 0x04, 0x62, 0x23, 0x15, 0x32, 0xcb, 0xa6, 0xf5, 0xea, 0x13, 0x69, - 0xa6, 0x33, 0xf0, 0xc7, 0xe8, 0xac, 0xed, 0xb8, 0x16, 0x6b, 0xb6, 0x36, 0x4d, 0xa7, 0xcd, 0x78, - 0x7c, 0x06, 0x63, 0x50, 0x8d, 0x90, 0xff, 0xa0, 0x5b, 0x04, 0x15, 0xc4, 0xe8, 0x9c, 0x9c, 0x9e, - 0x1a, 0x4a, 0x68, 0x9c, 0x85, 0x77, 0x91, 0x36, 0x56, 0x9a, 0x7d, 0xcf, 0xb4, 0x3b, 0xcc, 0x13, - 0xf1, 0xfa, 0xdb, 0x18, 0x04, 0xec, 0xc3, 0x41, 0x60, 0x5c, 0x88, 0x38, 0x6b, 0x82, 0x22, 0x83, - 0x75, 0xf9, 0xd0, 0xc8, 0xd2, 0xb4, 0x2a, 0x23, 0x8e, 0x37, 0xc6, 0xef, 0xf2, 0x53, 0x24, 0x3f, - 0xe9, 0x5a, 0xf2, 0x48, 0x7b, 0x45, 0x9c, 0x17, 0x01, 0x52, 0xad, 0x48, 0xca, 0x70, 0x60, 0x84, - 0x5f, 0x98, 0xa2, 0x31, 0xdb, 0xd9, 0x36, 0x3b, 0x76, 0x78, 0x64, 0x7d, 0xef, 0x59, 0x60, 0x20, - 0x6a, 0xee, 0x54, 0x05, 0x2a, 0x4e, 0x10, 0xf0, 0x53, 0x3b, 0x41, 0x80, 0xcc, 0x4f, 0x10, 0x1a, - 0x93, 0x86, 0x3c, 0xde, 0x56, 0x1c, 0x37, 0x76, 0x2b, 0xc8, 0x82, 0x6b, 0xd8, 0x56, 0xc7, 0x8d, - 0xdf, 0x08, 0xc4, 0xb6, 0xc6, 0x50, 0x42, 0xe3, 0xac, 0xf7, 0x53, 0x3f, 0xff, 0xca, 0x18, 0x21, - 0xdf, 0x26, 0xd0, 0xb8, 0x6a, 0x71, 0x7c, 0xba, 0x40, 0xfc, 0x93, 0x10, 0x7e, 0xa8, 0xe6, 0x4d, - 0x11, 0x77, 0x51, 0xcd, 0x9b, 0x10, 0x70, 0xc0, 0xf8, 0xf4, 0x74, 0x37, 0x36, 0x7c, 0xd6, 0x87, - 0xb9, 0x95, 0x14, 0xd3, 0x53, 0x20, 0x6a, 0x7a, 0x0a, 0x91, 0x50, 0x89, 0xe3, 0x37, 0xe5, 0xf4, - 0x1a, 0x85, 0xb0, 0x5d, 0x3d, 0x7e, 0x7a, 0x85, 0x41, 0x11, 0x43, 0xec, 0x26, 0x1a, 0xdf, 0x61, - 0xe6, 0x63, 0x91, 0x97, 0xa2, 0x65, 0x40, 0x5f, 0xe7, 0xa0, 0xcc, 0x49, 0x51, 0x1d, 0x21, 0x40, - 0xa8, 0xd2, 0xc9, 0x77, 0x7c, 0x84, 0x32, 0x62, 0x9c, 0xe0, 0x55, 0x94, 0x6d, 0xb9, 0x5b, 0x4e, - 0x3f, 0xba, 0x54, 0x4e, 0xeb, 0xa7, 0x61, 0xd0, 0x94, 0xfe, 0x2d, 0x2c, 0xc0, 0x90, 0xaa, 0x62, - 0x24, 0x01, 0x7e, 0x8c, 0x95, 0x2a, 0xf2, 0x59, 0x02, 0x8d, 0x49, 0x43, 0x7c, 0x5b, 0x5d, 0x0e, - 0x52, 0xa5, 0xf7, 0x0e, 0x4d, 0xc9, 0xef, 0xbe, 0x68, 0xea, 0x13, 0x52, 0xde, 0x39, 0xb7, 0xcd, - 0xce, 0x96, 0xd8, 0xa8, 0x94, 0xb8, 0x73, 0x02, 0xa0, 0x86, 0x0e, 0x48, 0x84, 0x0a, 0x94, 0x7c, - 0x96, 0x42, 0x93, 0x7a, 0x13, 0xe1, 0xed, 0x7a, 0xcb, 0xb1, 0x77, 0x61, 0x31, 0xb1, 0x53, 0xca, - 0x3d, 0xc7, 0xde, 0x85, 0x36, 0x53, 0x78, 0x12, 0x18, 0x09, 0x1e, 0x00, 0xce, 0x53, 0x01, 0xe0, - 0x02, 0xa1, 0x80, 0xe1, 0x8f, 0xd1, 0xd8, 0x8e, 0xed, 0x58, 0xee, 0x8e, 0x0f, 0xcb, 0x98, 0xd0, - 0x6f, 0x0e, 0x0f, 0x84, 0x02, 0x3c, 0x15, 0xa5, 0xa7, 0x90, 0xad, 0xb6, 0x4b, 0xca, 0x84, 0x86, - 0x1a, 0xbc, 0x8c, 0xd2, 0x1d, 0xdb, 0xd9, 0xda, 0x85, 0x04, 0x8b, 0x8d, 0xd9, 0x4f, 0xcc, 0x7e, - 0xdf, 0x03, 0x77, 0x57, 0xa4, 0x3b, 0xc1, 0x8c, 0x2e, 0xd9, 0x5c, 0xe2, 0x97, 0x6c, 0xfe, 0x2f, - 0xbe, 0x83, 0x32, 0x96, 0xe9, 0xed, 0xd8, 0xe2, 0x52, 0x73, 0x82, 0xa7, 0x19, 0xe9, 0x49, 0x52, - 0xa3, 0x0b, 0x1e, 0x88, 0x84, 0x4a, 0x1c, 0x33, 0x34, 0xb6, 0xe1, 0x31, 0xb6, 0xee, 0x5b, 0x70, - 0x48, 0x3a, 0xc1, 0xdb, 0xbb, 0xdc, 0x1b, 0xbf, 0x06, 0x2c, 0x79, 0x8c, 0x95, 0x1a, 0x70, 0x0d, - 0x90, 0x66, 0xea, 0x8d, 0xa5, 0x0c, 0xd7, 0x00, 0x49, 0xa3, 0x21, 0x09, 0x37, 0x51, 0xc6, 0x61, - 0x7d, 0xfe, 0x94, 0xcc, 0xc9, 0x4f, 0x99, 0x97, 0x4f, 0xc9, 0xd4, 0x58, 0x5f, 0x3c, 0x44, 0x1a, - 0xa9, 0xd5, 0x0b, 0x91, 0x3f, 0x42, 0x72, 0xa8, 0x64, 0x90, 0xcf, 0x47, 0x51, 0x36, 0x8c, 0x2f, - 0x3f, 0xfc, 0xb9, 0x3b, 0x0e, 0xf3, 0xf4, 0xaf, 0x5b, 0x30, 0xf1, 0x01, 0x95, 0xd7, 0x33, 0x31, - 0xc8, 0x14, 0x42, 0x68, 0xa4, 0xe5, 0x0e, 0xda, 0x9e, 0xbb, 0xd5, 0xd3, 0xbf, 0x6c, 0x81, 0x03, - 0x40, 0x63, 0x0e, 0x14, 0x42, 0x68, 0xa4, 0xc5, 0x37, 0x51, 0x72, 0xcb, 0xb6, 0x20, 0xd4, 0xe9, - 0xd2, 0xab, 0xcf, 0x02, 0x23, 0x79, 0x0f, 0x2a, 0x80, 0xa3, 0xc3, 0xc0, 0x18, 0x17, 0x09, 0x67, - 0x5b, 0xda, 0xf8, 0xe4, 0x0c, 0xca, 0xf5, 0xdc, 0xb8, 0x6d, 0x5b, 0x10, 0x5d, 0x69, 0xbc, 0x2c, - 0x8c, 0xdb, 0x9a, 0x71, 0x3b, 0x6e, 0xbc, 0xcc, 0x8d, 0x39, 0xf6, 0xcb, 0x04, 0x9a, 0xd0, 0x32, - 0xf4, 0x87, 0xef, 0xc5, 0x0a, 0x3a, 0x23, 0x1c, 0xd8, 0x7e, 0x13, 0x5e, 0x10, 0xf6, 0x43, 0x7e, - 0x36, 0x01, 0x4d, 0xd5, 0x5f, 0xe6, 0xb8, 0xfa, 0x6c, 0xa2, 0x83, 0x84, 0xc6, 0x38, 0xa4, 0x81, - 0xc6, 0x55, 0xc0, 0xf1, 0x12, 0xca, 0xec, 0x72, 0x21, 0x6c, 0x48, 0x67, 0x0f, 0x65, 0x45, 0x74, - 0xec, 0x14, 0x34, 0x55, 0x10, 0x20, 0x12, 0x2a, 0x61, 0xd2, 0x42, 0x69, 0xe0, 0xbf, 0xd0, 0x6d, - 0x22, 0xd6, 0x67, 0x26, 0xff, 0x79, 0x9f, 0xf9, 0x71, 0x0a, 0x8d, 0x51, 0x7e, 0x68, 0xf6, 0xfb, - 0xf8, 0x1d, 0xd5, 0xed, 0xd2, 0xa5, 0x57, 0x4e, 0x6a, 0x6f, 0x51, 0x74, 0xc2, 0xaf, 0x1f, 0xd1, - 0xa5, 0x6b, 0xf4, 0xd4, 0x97, 0xae, 0xf0, 0x95, 0x92, 0xa7, 0x78, 0xa5, 0x68, 0x2c, 0xa5, 0x5e, - 0x78, 0x2c, 0xa5, 0x4f, 0x3f, 0x96, 0xc2, 0x49, 0x99, 0x39, 0xc5, 0xa4, 0xac, 0xa3, 0x33, 0x1b, - 0x9e, 0xdb, 0x85, 0x6f, 0x64, 0xae, 0x67, 0x7a, 0x7b, 0xf2, 0x54, 0x00, 0xa3, 0x9b, 0x6b, 0xd6, - 0x42, 0x85, 0x1a, 0xdd, 0x31, 0x94, 0xd0, 0x38, 0x2b, 0x3e, 0x13, 0xb3, 0x2f, 0x36, 0x13, 0xf1, - 0x2d, 0x94, 0x15, 0x27, 0x5e, 0xc7, 0x85, 0x6b, 0x57, 0xba, 0xf4, 0x32, 0x6f, 0x65, 0x80, 0xd5, - 0x5c, 0xd5, 0xca, 0xa4, 0xac, 0x5e, 0x3b, 0x24, 0x90, 0xdf, 0x26, 0x50, 0x96, 0x32, 0xbf, 0xe7, - 0x3a, 0x3e, 0xfb, 0xbe, 0x49, 0x30, 0x87, 0x52, 0x96, 0xd9, 0x37, 0x65, 0xda, 0xc1, 0xee, 0x71, - 0x59, 0xed, 0x1e, 0x17, 0x08, 0x05, 0x0c, 0x7f, 0x88, 0x52, 0x2d, 0xd7, 0x12, 0xc1, 0x3f, 0xa3, - 0x37, 0xcd, 0x8a, 0xe7, 0xb9, 0xde, 0xa2, 0x6b, 0xc9, 0x6b, 0x07, 0x27, 0x29, 0x07, 0x5c, 0x20, - 0x14, 0x30, 0xf2, 0x9b, 0x04, 0xca, 0x95, 0xdd, 0x1d, 0xa7, 0xe3, 0x9a, 0xd6, 0xaa, 0xe7, 0xb6, - 0x3d, 0xe6, 0xfb, 0xdf, 0xeb, 0xee, 0xdf, 0x44, 0x63, 0x5b, 0xf0, 0xe5, 0x20, 0xbc, 0xfd, 0x5f, - 0x8b, 0x5f, 0x83, 0x0e, 0x3f, 0x44, 0x7c, 0x66, 0x88, 0x3e, 0x34, 0x4a, 0x63, 0xe5, 0x5f, 0xc8, - 0x84, 0x86, 0x0a, 0xf2, 0xeb, 0x24, 0x2a, 0x9c, 0xec, 0x08, 0x77, 0xd1, 0x84, 0x60, 0x36, 0xb5, - 0x4f, 0xfa, 0xb3, 0xa7, 0x59, 0x03, 0x5c, 0xce, 0xe0, 0x52, 0xb0, 0xa5, 0x64, 0x75, 0x29, 0x88, - 0x20, 0x42, 0x35, 0xfd, 0x0b, 0x7d, 0xa7, 0xd4, 0xae, 0xf2, 0xc9, 0x1f, 0x7e, 0x95, 0x6f, 0xa0, - 0x29, 0x91, 0xa2, 0xe1, 0x07, 0xe5, 0x54, 0x31, 0x39, 0x9b, 0x2e, 0x5d, 0xe7, 0xdd, 0x76, 0x5d, - 0x1c, 0x56, 0xc3, 0x4f, 0xc9, 0xd3, 0x51, 0xb2, 0x0a, 0x30, 0xcc, 0xb6, 0xdc, 0x08, 0x8d, 0x71, - 0xf1, 0x52, 0xec, 0xa6, 0x27, 0x4a, 0xfd, 0x3f, 0x4e, 0x79, 0xb3, 0xd3, 0x6e, 0x72, 0x24, 0x83, - 0x52, 0xab, 0xb6, 0xd3, 0x26, 0x37, 0x51, 0x7a, 0xb1, 0xe3, 0xfa, 0xd0, 0x71, 0x3c, 0x66, 0xfa, - 0xae, 0xa3, 0xa7, 0x92, 0x40, 0x54, 0xa8, 0x85, 0x48, 0xa8, 0xc4, 0xe7, 0xbe, 0x4e, 0xa2, 0x09, - 0xed, 0x2f, 0x30, 0xf8, 0x7f, 0xd0, 0xe5, 0xbb, 0x95, 0x46, 0x63, 0x61, 0xb9, 0xd2, 0x5c, 0x7b, - 0xb8, 0x5a, 0x69, 0x2e, 0xae, 0xdc, 0x6b, 0xac, 0x55, 0x68, 0x73, 0xb1, 0x5e, 0x5b, 0xaa, 0x2e, - 0xe7, 0x46, 0x0a, 0x57, 0xf6, 0x0f, 0x8a, 0x79, 0xcd, 0x22, 0xfe, 0xb7, 0x92, 0xff, 0x44, 0x38, - 0x66, 0x5e, 0xad, 0x95, 0x2b, 0x9f, 0xe4, 0x12, 0x85, 0xf3, 0xfb, 0x07, 0xc5, 0x9c, 0x66, 0x25, - 0x3e, 0xc1, 0xfd, 0x37, 0x7a, 0xe9, 0x28, 0xbb, 0x79, 0x6f, 0xb5, 0xbc, 0xb0, 0x56, 0xc9, 0x8d, - 0x16, 0x0a, 0xfb, 0x07, 0xc5, 0x8b, 0x87, 0x8d, 0x64, 0x0a, 0xbe, 0x81, 0xce, 0xc7, 0x4c, 0x69, - 0xe5, 0xe3, 0x7b, 0x95, 0xc6, 0x5a, 0x2e, 0x59, 0xb8, 0xb8, 0x7f, 0x50, 0xc4, 0x9a, 0x55, 0x38, - 0x26, 0xe6, 0xd1, 0x85, 0x43, 0x16, 0x8d, 0xd5, 0x7a, 0xad, 0x51, 0xc9, 0xa5, 0x0a, 0x97, 0xf6, - 0x0f, 0x8a, 0xe7, 0x62, 0x26, 0xb2, 0xab, 0x2c, 0xa2, 0x99, 0x98, 0x4d, 0xb9, 0xfe, 0xa0, 0xb6, - 0x52, 0x5f, 0x28, 0x37, 0x57, 0x69, 0x7d, 0x99, 0x56, 0x1a, 0x8d, 0x5c, 0xba, 0x60, 0xec, 0x1f, - 0x14, 0x2f, 0x6b, 0xc6, 0x47, 0x2a, 0x7c, 0x0e, 0x4d, 0xc7, 0x9c, 0xac, 0x56, 0x6b, 0xcb, 0xb9, - 0x4c, 0xe1, 0xdc, 0xfe, 0x41, 0xf1, 0xac, 0x66, 0xc7, 0x63, 0x79, 0x64, 0xff, 0x16, 0x57, 0xea, - 0x8d, 0x4a, 0x6e, 0xec, 0xc8, 0xfe, 0x41, 0xc0, 0xe7, 0x7e, 0x95, 0x40, 0xf8, 0xe8, 0x1f, 0xbd, - 0xf0, 0x7b, 0x28, 0x1f, 0x3a, 0x59, 0xac, 0xdf, 0x5d, 0xe5, 0xeb, 0xac, 0xd6, 0x6b, 0xcd, 0x5a, - 0xbd, 0x56, 0xc9, 0x8d, 0xc4, 0x76, 0x55, 0xb3, 0xaa, 0xb9, 0x0e, 0xc3, 0x75, 0x74, 0xe9, 0x38, - 0xcb, 0x95, 0x47, 0x6f, 0xe7, 0x12, 0x85, 0xf9, 0xfd, 0x83, 0xe2, 0x85, 0xa3, 0x86, 0x2b, 0x8f, - 0xde, 0xfe, 0xe6, 0xa7, 0xaf, 0x1c, 0xaf, 0x98, 0xe3, 0x07, 0x20, 0x7d, 0x69, 0x6f, 0xa2, 0xf3, - 0xba, 0xe3, 0xbb, 0x95, 0xb5, 0x85, 0xf2, 0xc2, 0xda, 0x42, 0x6e, 0x44, 0xc4, 0x40, 0xa3, 0xde, - 0x65, 0x7d, 0x13, 0xda, 0xee, 0x6b, 0x68, 0x3a, 0xf6, 0x16, 0x95, 0xfb, 0x15, 0x1a, 0x66, 0x94, - 0xbe, 0x7e, 0xb6, 0xcd, 0x3c, 0xfc, 0x3a, 0xc2, 0x3a, 0x79, 0x61, 0xe5, 0xc1, 0xc2, 0xc3, 0x46, - 0x6e, 0xb4, 0x70, 0x61, 0xff, 0xa0, 0x38, 0xad, 0xb1, 0x17, 0x3a, 0x3b, 0xe6, 0x9e, 0x3f, 0xf7, - 0xfb, 0x51, 0x34, 0xa9, 0x7f, 0x37, 0xc2, 0xaf, 0xa3, 0x73, 0x4b, 0xd5, 0x15, 0x9e, 0x89, 0x4b, - 0x75, 0x11, 0x01, 0x2e, 0xe6, 0x46, 0xc4, 0xe3, 0x74, 0x2a, 0xff, 0x8d, 0xff, 0x0b, 0xe5, 0x0f, - 0xd1, 0xcb, 0x55, 0x5a, 0x59, 0x5c, 0xab, 0xd3, 0x87, 0xb9, 0x44, 0xe1, 0x25, 0xbe, 0x61, 0xba, - 0x4d, 0xd9, 0xf6, 0xa0, 0x05, 0xed, 0xe1, 0x5b, 0xe8, 0xf2, 0x21, 0xc3, 0xc6, 0xc3, 0xbb, 0x2b, - 0xd5, 0xda, 0x1d, 0xf1, 0xbc, 0xd1, 0xc2, 0xd5, 0xfd, 0x83, 0xe2, 0x25, 0xdd, 0xb6, 0x21, 0x3e, - 0xc5, 0x71, 0x28, 0x9b, 0xc0, 0xb7, 0x51, 0xf1, 0x04, 0xfb, 0x68, 0x01, 0xc9, 0x02, 0xd9, 0x3f, - 0x28, 0x5e, 0x39, 0xc6, 0x89, 0x5a, 0x47, 0x36, 0x81, 0xdf, 0x42, 0x17, 0x8f, 0xf7, 0x14, 0xd6, - 0xc5, 0x31, 0xf6, 0x73, 0x7f, 0x4c, 0xa0, 0x71, 0x35, 0xf5, 0xf8, 0xa6, 0x55, 0x28, 0xad, 0xf3, - 0x26, 0x51, 0xae, 0x34, 0x6b, 0xf5, 0x26, 0x48, 0xe1, 0xa6, 0x29, 0x5e, 0xcd, 0x85, 0x9f, 0x3c, - 0xc7, 0x35, 0xfa, 0x72, 0xa5, 0x56, 0xa1, 0xd5, 0xc5, 0x30, 0xa2, 0x8a, 0xbd, 0xcc, 0x1c, 0xe6, - 0xd9, 0x2d, 0xfc, 0x36, 0xba, 0x14, 0x77, 0xde, 0xb8, 0xb7, 0x78, 0x3b, 0xdc, 0x25, 0x58, 0xa0, - 0xf6, 0x80, 0xc6, 0x56, 0x6b, 0x13, 0x02, 0xf3, 0x4e, 0xcc, 0xaa, 0x5a, 0xbb, 0xbf, 0xb0, 0x52, - 0x2d, 0x0b, 0xab, 0x64, 0x21, 0xbf, 0x7f, 0x50, 0x3c, 0xaf, 0xac, 0xe4, 0x07, 0x0e, 0x6e, 0x36, - 0xf7, 0x4d, 0x02, 0xcd, 0x7c, 0xf7, 0xf0, 0xc2, 0x0f, 0xd0, 0xab, 0xb0, 0x5f, 0x47, 0x5a, 0x81, - 0xec, 0x5b, 0x62, 0x0f, 0x17, 0x56, 0x57, 0x2b, 0xb5, 0x72, 0x6e, 0xa4, 0x30, 0xbb, 0x7f, 0x50, - 0xbc, 0xf6, 0xdd, 0x2e, 0x17, 0x7a, 0x3d, 0xe6, 0x58, 0xa7, 0x74, 0xbc, 0x54, 0xa7, 0xcb, 0x95, - 0xb5, 0x5c, 0xe2, 0x34, 0x8e, 0x97, 0x5c, 0xaf, 0xcd, 0xfa, 0xa5, 0xbb, 0x4f, 0xbe, 0x9d, 0x19, - 0x79, 0xfa, 0xed, 0xcc, 0xc8, 0x93, 0x67, 0x33, 0x89, 0xa7, 0xcf, 0x66, 0x12, 0x3f, 0x7b, 0x3e, - 0x33, 0xf2, 0xd5, 0xf3, 0x99, 0xc4, 0xd3, 0xe7, 0x33, 0x23, 0x7f, 0x7a, 0x3e, 0x33, 0xf2, 0xe8, - 0xb5, 0xb6, 0xdd, 0xdf, 0xdc, 0x5a, 0xbf, 0xde, 0x72, 0xbb, 0x37, 0xfc, 0x3d, 0xa7, 0xd5, 0xdf, - 0xb4, 0x9d, 0xb6, 0xf6, 0x4b, 0xff, 0xcf, 0x0f, 0xeb, 0x19, 0xf8, 0xf5, 0xd6, 0x3f, 0x02, 0x00, - 0x00, 0xff, 0xff, 0x68, 0x4a, 0x6e, 0xeb, 0x13, 0x21, 0x00, 0x00, + // 3183 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x5a, 0x4b, 0x6c, 0x1b, 0xc7, + 0xf9, 0x17, 0xc5, 0x87, 0xa8, 0xd1, 0xc3, 0xd4, 0xf8, 0xc5, 0xd0, 0xb6, 0x96, 0xff, 0x89, 0xf3, + 0xaf, 0xa2, 0x34, 0x76, 0xa2, 0x3c, 0x9a, 0xc6, 0xa9, 0x03, 0x51, 0xa4, 0x64, 0xc6, 0x32, 0xa9, + 0x0c, 0x65, 0x3b, 0x36, 0x50, 0x10, 0x2b, 0xee, 0x88, 0x5a, 0x98, 0xdc, 0x65, 0x77, 0xa9, 0x57, + 0xd0, 0x4b, 0x1b, 0x20, 0x08, 0x74, 0x28, 0x8a, 0x9c, 0x8a, 0x22, 0x42, 0x83, 0x5e, 0x7a, 0x2b, + 0xd0, 0x43, 0x2f, 0x39, 0xf5, 0xe8, 0xa3, 0x11, 0xa0, 0x40, 0xd1, 0xc3, 0x02, 0xb1, 0x2f, 0x2d, + 0x7b, 0xd3, 0xb1, 0xa7, 0x62, 0xbe, 0x99, 0x9d, 0x9d, 0xd5, 0x23, 0x95, 0x93, 0x43, 0x4f, 0xe6, + 0xf7, 0xfb, 0x7e, 0xdf, 0xb7, 0xb3, 0x33, 0xdf, 0x6b, 0x56, 0x46, 0x17, 0x3a, 0xf6, 0xda, 0xf5, + 0x9e, 0xe7, 0xf6, 0xdd, 0x96, 0xdb, 0xb9, 0xbe, 0xc6, 0x7a, 0xd7, 0x40, 0xc0, 0xd9, 0x10, 0x2b, + 0x8c, 0xb2, 0x9d, 0xbe, 0x00, 0x0b, 0x2f, 0x7a, 0xac, 0xe7, 0xfa, 0x82, 0xbe, 0xb6, 0xb9, 0x7e, + 0xbd, 0xed, 0xb6, 0x5d, 0x10, 0xe0, 0x97, 0x20, 0x91, 0xa7, 0x09, 0x94, 0xbe, 0xc5, 0x3a, 0x1d, + 0x17, 0x2f, 0xa0, 0x31, 0x8b, 0x6d, 0xd9, 0x2d, 0xd6, 0x74, 0xcc, 0x2e, 0xcb, 0x27, 0x8a, 0x89, + 0x99, 0xd1, 0x12, 0x19, 0x04, 0x06, 0x12, 0x70, 0xcd, 0xec, 0xb2, 0x83, 0xc0, 0xc8, 0xed, 0x74, + 0x3b, 0xef, 0x92, 0x08, 0x22, 0x54, 0xd3, 0x73, 0x27, 0xad, 0x8e, 0xcd, 0x9c, 0xbe, 0x70, 0x32, + 0x1c, 0x39, 0x11, 0x70, 0xcc, 0x49, 0x04, 0x11, 0xaa, 0xe9, 0x71, 0x1d, 0x4d, 0x4a, 0x27, 0x5b, + 0xcc, 0xf3, 0x6d, 0xd7, 0xc9, 0x27, 0xc1, 0xcf, 0xcc, 0x20, 0x30, 0x26, 0x84, 0xe6, 0x9e, 0x50, + 0x1c, 0x04, 0xc6, 0x59, 0xcd, 0x95, 0x44, 0x09, 0x8d, 0xb3, 0xc8, 0x9f, 0x12, 0x28, 0x73, 0x8b, + 0x99, 0x16, 0xf3, 0xf0, 0x3c, 0x4a, 0xf5, 0x77, 0x7b, 0xe2, 0xf5, 0x26, 0xe7, 0xce, 0x5f, 0x0b, + 0x37, 0xee, 0xda, 0x1d, 0xe6, 0xfb, 0x66, 0x9b, 0xad, 0xee, 0xf6, 0x58, 0xe9, 0xc2, 0x20, 0x30, + 0x80, 0x76, 0x10, 0x18, 0x08, 0xfc, 0x73, 0x81, 0x50, 0xc0, 0xb0, 0x85, 0xc6, 0x5a, 0x6e, 0xb7, + 0xe7, 0x31, 0x1f, 0xd6, 0x36, 0x0c, 0x9e, 0x2e, 0x1f, 0xf1, 0xb4, 0x10, 0x71, 0x4a, 0x57, 0x07, + 0x81, 0xa1, 0x1b, 0x1d, 0x04, 0xc6, 0x94, 0x58, 0x77, 0x84, 0x11, 0xaa, 0x33, 0xc8, 0x17, 0x09, + 0x34, 0xb1, 0xd0, 0xd9, 0xf4, 0xfb, 0xcc, 0x5b, 0x70, 0x9d, 0x75, 0xbb, 0x8d, 0x6f, 0xa3, 0x91, + 0x75, 0xb7, 0x63, 0x31, 0xcf, 0xcf, 0x27, 0x8a, 0xc9, 0x99, 0xb1, 0xb9, 0x5c, 0xf4, 0xcc, 0x45, + 0x50, 0x94, 0x8c, 0xc7, 0x81, 0x31, 0x34, 0x08, 0x8c, 0x90, 0x78, 0x10, 0x18, 0xe3, 0xf0, 0x1c, + 0x21, 0x13, 0x1a, 0x2a, 0xf0, 0x4d, 0x34, 0xea, 0xb3, 0x96, 0xeb, 0x58, 0xa6, 0xb7, 0x0b, 0xaf, + 0x90, 0x2d, 0x15, 0x07, 0x81, 0x11, 0x81, 0x07, 0x81, 0x71, 0x06, 0x4c, 0x15, 0x42, 0x68, 0xa4, + 0x25, 0x5f, 0xa5, 0x50, 0x46, 0x3c, 0x14, 0x5f, 0x43, 0xc3, 0xb6, 0x25, 0xe3, 0x65, 0xfa, 0x69, + 0x60, 0x0c, 0x57, 0xcb, 0x83, 0xc0, 0x18, 0xb6, 0xad, 0x83, 0xc0, 0xc8, 0x82, 0x0b, 0xdb, 0x22, + 0x9f, 0x3f, 0xb9, 0x3a, 0x5c, 0x2d, 0xd3, 0x61, 0xdb, 0xc2, 0xd7, 0x50, 0xba, 0x63, 0xae, 0xb1, + 0x8e, 0x8c, 0x8e, 0xfc, 0x20, 0x30, 0x04, 0x70, 0x10, 0x18, 0x63, 0xc0, 0x07, 0x89, 0x50, 0x81, + 0xe2, 0x1b, 0x68, 0xd4, 0x63, 0xa6, 0xd5, 0x74, 0x9d, 0xce, 0x2e, 0x44, 0x42, 0xb6, 0x34, 0x3d, + 0x08, 0x8c, 0x2c, 0x07, 0xeb, 0x4e, 0x87, 0xaf, 0x74, 0x12, 0xcc, 0x42, 0x80, 0x50, 0xa5, 0xc3, + 0x4d, 0x84, 0xed, 0xb6, 0xe3, 0x7a, 0xac, 0xd9, 0x63, 0x5e, 0xd7, 0x86, 0xbd, 0xf5, 0xf3, 0x29, + 0xf0, 0xf2, 0xda, 0x20, 0x30, 0xa6, 0x84, 0x76, 0x25, 0x52, 0x1e, 0x04, 0xc6, 0x45, 0xb1, 0xea, + 0xc3, 0x1a, 0x42, 0x8f, 0xb2, 0xf1, 0x6d, 0x34, 0x21, 0x1f, 0x60, 0xb1, 0x0e, 0xeb, 0xb3, 0x7c, + 0x1a, 0x7c, 0xff, 0xff, 0x20, 0x30, 0xc6, 0x85, 0xa2, 0x0c, 0xf8, 0x41, 0x60, 0x60, 0xcd, 0xad, + 0x00, 0x09, 0x8d, 0x71, 0xb0, 0x85, 0xce, 0x59, 0xb6, 0x6f, 0xae, 0x75, 0x58, 0xb3, 0xcf, 0xba, + 0xbd, 0xa6, 0xed, 0x58, 0x6c, 0x87, 0xf9, 0xf9, 0x0c, 0xf8, 0x9c, 0x1b, 0x04, 0x06, 0x96, 0xfa, + 0x55, 0xd6, 0xed, 0x55, 0x85, 0xf6, 0x20, 0x30, 0xf2, 0x22, 0x29, 0x8f, 0xa8, 0x08, 0x3d, 0x86, + 0x8f, 0xe7, 0x50, 0xa6, 0x67, 0x6e, 0xfa, 0xcc, 0xca, 0x8f, 0x80, 0xdf, 0xc2, 0x20, 0x30, 0x24, + 0xa2, 0x02, 0x46, 0x88, 0x84, 0x4a, 0x9c, 0x07, 0x9f, 0x48, 0x73, 0x3f, 0x9f, 0x3b, 0x1c, 0x7c, + 0x65, 0x50, 0x44, 0xc1, 0x27, 0x89, 0xca, 0x97, 0x90, 0x09, 0x0d, 0x15, 0xe4, 0x2f, 0x19, 0x94, + 0x11, 0x46, 0xb8, 0xa4, 0x82, 0x67, 0xbc, 0x34, 0xc7, 0x1d, 0xfc, 0x3d, 0x30, 0xb2, 0x42, 0x57, + 0x2d, 0x9f, 0x14, 0x4c, 0x9f, 0x3d, 0xb9, 0x9a, 0xd0, 0x02, 0x6a, 0x16, 0xa5, 0xb4, 0x6a, 0x03, + 0xc9, 0xeb, 0x88, 0x3a, 0x23, 0x92, 0xd7, 0x81, 0x0a, 0x03, 0x18, 0x7e, 0x0f, 0x8d, 0x9a, 0x96, + 0xc5, 0x93, 0x8c, 0xf9, 0xf9, 0x64, 0x31, 0xc9, 0x63, 0x96, 0xc7, 0xbd, 0x02, 0x0f, 0x02, 0x63, + 0x02, 0xac, 0x24, 0x42, 0x68, 0xa4, 0xc3, 0x3f, 0x8d, 0xa7, 0x7e, 0xea, 0x70, 0x11, 0xf9, 0x7e, + 0x39, 0xcf, 0x23, 0xbd, 0xc5, 0x3c, 0x59, 0x3b, 0xd3, 0x22, 0xa1, 0x78, 0xa4, 0x73, 0x50, 0x56, + 0x4e, 0x11, 0xe9, 0x21, 0x40, 0xa8, 0xd2, 0xe1, 0x25, 0x34, 0xde, 0x35, 0x77, 0x9a, 0x3e, 0xfb, + 0xd9, 0x26, 0x73, 0x5a, 0x0c, 0x62, 0x26, 0x29, 0x56, 0xd1, 0x35, 0x77, 0x1a, 0x12, 0x56, 0xab, + 0xd0, 0x30, 0x42, 0x75, 0x06, 0x2e, 0x21, 0x64, 0x3b, 0x7d, 0xcf, 0xb5, 0x36, 0x5b, 0xcc, 0x93, + 0x21, 0x02, 0x25, 0x3c, 0x42, 0x55, 0x09, 0x8f, 0x20, 0x42, 0x35, 0x3d, 0x6e, 0xa3, 0x2c, 0xc4, + 0x6e, 0xd3, 0xb6, 0xf2, 0xd9, 0x62, 0x62, 0x26, 0x55, 0x5a, 0x96, 0x87, 0x3b, 0x02, 0x51, 0x08, + 0x67, 0x1b, 0xfe, 0xe4, 0x31, 0x03, 0xec, 0xaa, 0xa5, 0x76, 0x5f, 0xca, 0xbc, 0x6e, 0x84, 0xb4, + 0xdf, 0x46, 0x3f, 0x69, 0xc8, 0xc7, 0x3f, 0x47, 0x05, 0xff, 0x91, 0xcd, 0x33, 0x45, 0x3c, 0xbb, + 0x6f, 0xbb, 0x4e, 0xd3, 0x63, 0x5d, 0x77, 0xcb, 0xec, 0xf8, 0xf9, 0x51, 0x58, 0xfc, 0xcd, 0x41, + 0x60, 0xe4, 0x39, 0xab, 0xaa, 0x91, 0xa8, 0xe4, 0x1c, 0x04, 0xc6, 0xb4, 0xa8, 0x73, 0x27, 0x10, + 0x08, 0x3d, 0xd1, 0x16, 0xef, 0xa0, 0x17, 0x98, 0xd3, 0xf2, 0x76, 0x7b, 0xf0, 0xd8, 0x9e, 0xe9, + 0xfb, 0xdb, 0xae, 0x67, 0x35, 0xfb, 0xee, 0x23, 0xe6, 0xe4, 0x11, 0x04, 0xf5, 0x7b, 0x83, 0xc0, + 0xb8, 0x18, 0x91, 0x56, 0x24, 0x67, 0x95, 0x53, 0x0e, 0x02, 0xe3, 0x0a, 0x3c, 0xfb, 0x04, 0x3d, + 0xa1, 0x27, 0x59, 0x92, 0x5f, 0x26, 0x50, 0x1a, 0x36, 0x83, 0x67, 0xb3, 0x28, 0xea, 0xb2, 0x04, + 0x43, 0x36, 0x0b, 0xe4, 0x48, 0xf9, 0x97, 0x38, 0xae, 0xa0, 0xf4, 0xba, 0xdd, 0x61, 0x7e, 0x7e, + 0x18, 0x72, 0x19, 0x6b, 0x8d, 0xc4, 0xee, 0xb0, 0xaa, 0xb3, 0xee, 0x96, 0x2e, 0xc9, 0x6c, 0x16, + 0x44, 0x95, 0x4b, 0x5c, 0x22, 0x54, 0x80, 0xe4, 0xb3, 0x04, 0x1a, 0x83, 0x45, 0xdc, 0xed, 0x59, + 0x66, 0x9f, 0xfd, 0x2f, 0x97, 0xf2, 0xe9, 0x04, 0xca, 0x86, 0x06, 0xaa, 0x20, 0x24, 0x4e, 0x51, + 0x10, 0x66, 0x51, 0xca, 0xb7, 0x3f, 0x66, 0xd0, 0x58, 0x92, 0x82, 0xcb, 0x65, 0xc5, 0xe5, 0x02, + 0xa1, 0x80, 0xe1, 0xf7, 0x11, 0xea, 0xba, 0x96, 0xbd, 0x6e, 0x33, 0xab, 0xe9, 0x43, 0x82, 0x26, + 0x45, 0xd7, 0x0c, 0xd1, 0x86, 0xea, 0x9a, 0x0a, 0x21, 0x34, 0xd2, 0xf2, 0xfa, 0xa1, 0x1c, 0xac, + 0xed, 0xe6, 0xc7, 0x21, 0x33, 0xde, 0x0b, 0x33, 0xa3, 0xb1, 0xe1, 0x7a, 0x7d, 0x48, 0x07, 0xf5, + 0x98, 0xd2, 0xae, 0x4a, 0xb5, 0x08, 0x22, 0x3c, 0x13, 0x24, 0x99, 0x6a, 0x54, 0xbc, 0x8c, 0x46, + 0xc2, 0x89, 0x89, 0x47, 0x7e, 0xac, 0x48, 0xdf, 0x63, 0xad, 0xbe, 0xeb, 0x95, 0x8a, 0x61, 0x91, + 0xde, 0x52, 0x13, 0x94, 0x48, 0xb8, 0xad, 0x70, 0x76, 0x0a, 0x35, 0xf8, 0x5d, 0x94, 0x55, 0xc5, + 0x04, 0xc1, 0xbb, 0x42, 0x31, 0xf2, 0xa3, 0x4a, 0x32, 0x29, 0x07, 0x84, 0xb0, 0x8c, 0x28, 0x1d, + 0xfe, 0x00, 0x65, 0xd6, 0x3a, 0x6e, 0xeb, 0x51, 0xd8, 0x2d, 0xce, 0x46, 0x0b, 0x29, 0x71, 0x1c, + 0xce, 0xf5, 0x8a, 0x5c, 0x8b, 0xa4, 0xaa, 0xf6, 0x0f, 0x22, 0xa1, 0x12, 0xe6, 0xe3, 0xa0, 0xbf, + 0xdb, 0xed, 0xd8, 0xce, 0xa3, 0x66, 0xdf, 0xf4, 0xda, 0xac, 0x9f, 0x9f, 0x8a, 0xc6, 0x41, 0xa9, + 0x59, 0x05, 0x85, 0x1a, 0x07, 0x63, 0x28, 0xa1, 0x71, 0x16, 0x1f, 0x52, 0x85, 0xeb, 0xe6, 0x86, + 0xe9, 0x6f, 0xe4, 0x31, 0xe4, 0x29, 0x54, 0x38, 0x01, 0xdf, 0x32, 0xfd, 0x0d, 0xb5, 0xed, 0x11, + 0x44, 0xa8, 0xa6, 0xe7, 0x03, 0x94, 0xcc, 0x4d, 0x66, 0xe5, 0xcf, 0x82, 0x0b, 0x08, 0x05, 0x05, + 0xaa, 0x50, 0x50, 0x08, 0xa1, 0x91, 0x16, 0x97, 0xe4, 0x20, 0x2a, 0xc6, 0xc7, 0x0b, 0x47, 0xc3, + 0xfe, 0x14, 0x93, 0xe8, 0x22, 0x1a, 0x3b, 0x3c, 0xd5, 0x4c, 0x88, 0x8a, 0xdf, 0x8b, 0xcd, 0x33, + 0xa2, 0xe2, 0xf7, 0xf4, 0x49, 0x46, 0x67, 0xe0, 0x0f, 0xb4, 0xb0, 0x74, 0xfc, 0xfc, 0x58, 0x31, + 0x31, 0x93, 0x2e, 0xbd, 0xac, 0xc7, 0x61, 0xcd, 0x3f, 0x12, 0x87, 0x35, 0x9f, 0xfc, 0x3b, 0x30, + 0x92, 0xb6, 0xd3, 0xa7, 0x1a, 0x0d, 0xaf, 0x23, 0xb1, 0x4b, 0x4d, 0xc8, 0xaa, 0x09, 0x70, 0xb5, + 0xf4, 0x34, 0x30, 0xc6, 0xa9, 0xb9, 0x0d, 0x47, 0xdf, 0xb0, 0x3f, 0x66, 0x7c, 0xa3, 0xd6, 0x42, + 0x41, 0x6d, 0x94, 0x42, 0x42, 0xc7, 0x9f, 0x3f, 0xb9, 0x1a, 0x33, 0xa3, 0x91, 0x11, 0xbe, 0x87, + 0xb2, 0xbd, 0x8e, 0xd9, 0x5f, 0x77, 0xbd, 0x6e, 0x7e, 0x12, 0x82, 0x5d, 0xdb, 0xc3, 0x15, 0xa9, + 0x29, 0x9b, 0x7d, 0xb3, 0x44, 0x64, 0x98, 0x29, 0xbe, 0x8a, 0xdc, 0x10, 0x20, 0x54, 0xe9, 0x70, + 0x19, 0x8d, 0x75, 0xdc, 0x96, 0xd9, 0x69, 0xae, 0x77, 0xcc, 0xb6, 0x9f, 0xff, 0xc7, 0x08, 0x6c, + 0x2a, 0x44, 0x07, 0xe0, 0x8b, 0x1c, 0x56, 0x9b, 0x11, 0x41, 0x84, 0x6a, 0x7a, 0x7c, 0x0b, 0x8d, + 0xcb, 0x34, 0x12, 0x31, 0xf6, 0xcf, 0x11, 0x88, 0x10, 0x38, 0x1b, 0xa9, 0x90, 0x51, 0x36, 0xa5, + 0x67, 0x9f, 0x08, 0x33, 0x9d, 0x81, 0x3f, 0x44, 0x67, 0x6c, 0xc7, 0xb5, 0x58, 0xb3, 0xb5, 0x61, + 0x3a, 0x6d, 0xc6, 0xcf, 0x67, 0x30, 0x02, 0xd9, 0x08, 0xf1, 0x0f, 0xba, 0x05, 0x50, 0xc1, 0x19, + 0x9d, 0x95, 0xdd, 0x53, 0x43, 0x09, 0x8d, 0xb3, 0xf0, 0x0e, 0xd2, 0xda, 0x4a, 0xb3, 0xef, 0x99, + 0x76, 0x87, 0x79, 0xe2, 0xbc, 0xfe, 0x35, 0x02, 0x07, 0xf6, 0xfe, 0x20, 0x30, 0xce, 0x47, 0x9c, + 0x55, 0x41, 0x91, 0x87, 0x75, 0xe9, 0x50, 0xcb, 0xd2, 0xb4, 0x2a, 0x22, 0x8e, 0x37, 0xc6, 0x6f, + 0xf3, 0x29, 0x92, 0x4f, 0xba, 0x96, 0x1c, 0x69, 0x2f, 0x8b, 0x79, 0x11, 0x20, 0x55, 0x8a, 0xa4, + 0x0c, 0x03, 0x23, 0xfc, 0xc2, 0x14, 0x8d, 0xd8, 0xce, 0x96, 0xd9, 0xb1, 0xc3, 0x91, 0xf5, 0x9d, + 0xa7, 0x81, 0x81, 0xa8, 0xb9, 0x5d, 0x15, 0xa8, 0x98, 0x20, 0xe0, 0xa7, 0x36, 0x41, 0x80, 0xcc, + 0x27, 0x08, 0x8d, 0x49, 0x43, 0x1e, 0x2f, 0x2b, 0x8e, 0x1b, 0xbb, 0x15, 0x64, 0xc1, 0x35, 0x6c, + 0xab, 0xe3, 0xc6, 0x6f, 0x04, 0x62, 0x5b, 0x63, 0x28, 0xa1, 0x71, 0xd6, 0xbb, 0xa9, 0xdf, 0x7c, + 0x69, 0x0c, 0x91, 0x6f, 0x12, 0x68, 0x54, 0x95, 0x38, 0xde, 0x5d, 0xe0, 0xfc, 0x93, 0x70, 0xfc, + 0x90, 0xcd, 0x1b, 0xe2, 0xdc, 0x45, 0x36, 0x6f, 0xc0, 0x81, 0x03, 0xc6, 0xbb, 0xa7, 0xbb, 0xbe, + 0xee, 0xb3, 0x3e, 0xf4, 0xad, 0xa4, 0xe8, 0x9e, 0x02, 0x51, 0xdd, 0x53, 0x88, 0x84, 0x4a, 0x1c, + 0xbf, 0x2e, 0xbb, 0xd7, 0x30, 0x1c, 0xdb, 0x95, 0xe3, 0xbb, 0x57, 0x78, 0x28, 0xa2, 0x89, 0xdd, + 0x40, 0xa3, 0xdb, 0xcc, 0x7c, 0x24, 0xe2, 0x52, 0x94, 0x0c, 0xa8, 0xeb, 0x1c, 0x94, 0x31, 0x29, + 0xb2, 0x23, 0x04, 0x08, 0x55, 0x3a, 0xf9, 0x8e, 0x0f, 0x51, 0x46, 0xb4, 0x13, 0xbc, 0x82, 0xb2, + 0x2d, 0x77, 0xd3, 0xe9, 0x47, 0x97, 0xd2, 0x29, 0x7d, 0x1a, 0x06, 0x4d, 0xe9, 0xff, 0xc2, 0x04, + 0x0c, 0xa9, 0xea, 0x8c, 0x24, 0xc0, 0xc7, 0x58, 0xa9, 0x22, 0x9f, 0x24, 0xd0, 0x88, 0x34, 0xc4, + 0xb7, 0xd4, 0xe5, 0x20, 0x55, 0x7a, 0xe7, 0x50, 0x97, 0xfc, 0xf6, 0x8b, 0xa6, 0xde, 0x21, 0xe5, + 0x9d, 0x73, 0xcb, 0xec, 0x6c, 0x8a, 0x8d, 0x4a, 0x89, 0x3b, 0x27, 0x00, 0xaa, 0xe9, 0x80, 0x44, + 0xa8, 0x40, 0xc9, 0x27, 0x29, 0x34, 0xae, 0x17, 0x11, 0x5e, 0xae, 0x37, 0x1d, 0x7b, 0x07, 0x16, + 0x13, 0x9b, 0x52, 0xee, 0x3a, 0xf6, 0x0e, 0x94, 0x99, 0xc2, 0xe3, 0xc0, 0x48, 0xf0, 0x03, 0xe0, + 0x3c, 0x75, 0x00, 0x5c, 0x20, 0x14, 0x30, 0xfc, 0x21, 0x1a, 0xd9, 0xb6, 0x1d, 0xcb, 0xdd, 0xf6, + 0x61, 0x19, 0x63, 0xfa, 0xcd, 0xe1, 0xbe, 0x50, 0x80, 0xa7, 0xa2, 0xf4, 0x14, 0xb2, 0xd5, 0x76, + 0x49, 0x99, 0xd0, 0x50, 0x83, 0x97, 0x50, 0xba, 0x63, 0x3b, 0x9b, 0x3b, 0x10, 0x60, 0xb1, 0x36, + 0xfb, 0x91, 0xd9, 0xef, 0x7b, 0xe0, 0xee, 0xb2, 0x74, 0x27, 0x98, 0xd1, 0x25, 0x9b, 0x4b, 0xfc, + 0x92, 0xcd, 0xff, 0xc5, 0xb7, 0x51, 0xc6, 0x32, 0xbd, 0x6d, 0x5b, 0x5c, 0x6a, 0x4e, 0xf0, 0x34, + 0x2d, 0x3d, 0x49, 0x6a, 0x74, 0xc1, 0x03, 0x91, 0x50, 0x89, 0x63, 0x86, 0x46, 0xd6, 0x3d, 0xc6, + 0xd6, 0x7c, 0x0b, 0x86, 0xa4, 0x13, 0xbc, 0xbd, 0xcd, 0xbd, 0xf1, 0x6b, 0xc0, 0xa2, 0xc7, 0x58, + 0xa9, 0x01, 0xd7, 0x00, 0x69, 0xa6, 0xde, 0x58, 0xca, 0x70, 0x0d, 0x90, 0x34, 0x1a, 0x92, 0x70, + 0x13, 0x65, 0x1c, 0xd6, 0xe7, 0x4f, 0xc9, 0x9c, 0xfc, 0x94, 0x39, 0xf9, 0x94, 0x4c, 0x8d, 0xf5, + 0xc5, 0x43, 0xa4, 0x91, 0x5a, 0xbd, 0x10, 0xf9, 0x23, 0x24, 0x87, 0x4a, 0x06, 0xf9, 0x74, 0x18, + 0x65, 0xc3, 0xf3, 0xe5, 0xc3, 0x9f, 0xbb, 0xed, 0x30, 0x4f, 0xff, 0x3c, 0x06, 0x1d, 0x1f, 0x50, + 0x79, 0x3d, 0x13, 0x8d, 0x4c, 0x21, 0x84, 0x46, 0x5a, 0xee, 0xa0, 0xed, 0xb9, 0x9b, 0x3d, 0xfd, + 0xd3, 0x18, 0x38, 0x00, 0x34, 0xe6, 0x40, 0x21, 0x84, 0x46, 0x5a, 0x7c, 0x03, 0x25, 0x37, 0x6d, + 0x0b, 0x8e, 0x3a, 0x5d, 0x7a, 0xf9, 0x69, 0x60, 0x24, 0xef, 0x42, 0x06, 0x70, 0xf4, 0x20, 0x30, + 0x46, 0x45, 0xc0, 0xd9, 0x96, 0xd6, 0x3e, 0x39, 0x83, 0x72, 0x3d, 0x37, 0x6e, 0xdb, 0x16, 0x9c, + 0xae, 0x34, 0x5e, 0x12, 0xc6, 0x6d, 0xcd, 0xb8, 0x1d, 0x37, 0x5e, 0xe2, 0xc6, 0x1c, 0xfb, 0x22, + 0x81, 0xc6, 0xb4, 0x08, 0xfd, 0xfe, 0x7b, 0xb1, 0x8c, 0x26, 0x85, 0x03, 0xdb, 0x6f, 0xc2, 0x0b, + 0xca, 0x6f, 0x50, 0xf0, 0xd9, 0x04, 0x34, 0x55, 0x7f, 0x89, 0xe3, 0xea, 0xb3, 0x89, 0x0e, 0x12, + 0x1a, 0xe3, 0x90, 0x06, 0x1a, 0x55, 0x07, 0x8e, 0x17, 0x51, 0x66, 0x87, 0x0b, 0x61, 0x41, 0x3a, + 0x73, 0x28, 0x2a, 0xa2, 0xb1, 0x53, 0xd0, 0x54, 0x42, 0x80, 0x48, 0xa8, 0x84, 0x49, 0x0b, 0xa5, + 0x81, 0xff, 0x5c, 0xb7, 0x89, 0x58, 0x9d, 0x19, 0xff, 0xef, 0x75, 0xe6, 0x17, 0x29, 0x34, 0x42, + 0xf9, 0xd0, 0xec, 0xf7, 0xf1, 0x5b, 0xaa, 0xda, 0xa5, 0x4b, 0x2f, 0x9d, 0x54, 0xde, 0xa2, 0xd3, + 0x09, 0xbf, 0x7e, 0x44, 0x97, 0xae, 0xe1, 0x53, 0x5f, 0xba, 0xc2, 0x57, 0x4a, 0x9e, 0xe2, 0x95, + 0xa2, 0xb6, 0x94, 0x7a, 0xee, 0xb6, 0x94, 0x3e, 0x7d, 0x5b, 0x0a, 0x3b, 0x65, 0xe6, 0x14, 0x9d, + 0xb2, 0x8e, 0x26, 0xd7, 0x3d, 0xb7, 0x0b, 0xdf, 0xc8, 0x5c, 0xcf, 0xf4, 0x76, 0xe5, 0x54, 0x00, + 0xad, 0x9b, 0x6b, 0x56, 0x43, 0x85, 0x6a, 0xdd, 0x31, 0x94, 0xd0, 0x38, 0x2b, 0xde, 0x13, 0xb3, + 0xcf, 0xd7, 0x13, 0xf1, 0x4d, 0x94, 0x15, 0x13, 0xaf, 0xe3, 0xc2, 0xb5, 0x2b, 0x5d, 0x7a, 0x91, + 0x97, 0x32, 0xc0, 0x6a, 0xae, 0x2a, 0x65, 0x52, 0x56, 0xaf, 0x1d, 0x12, 0xc8, 0x1f, 0x13, 0x28, + 0x4b, 0x99, 0xdf, 0x73, 0x1d, 0x9f, 0x7d, 0xd7, 0x20, 0x98, 0x45, 0x29, 0xcb, 0xec, 0x9b, 0x32, + 0xec, 0x60, 0xf7, 0xb8, 0xac, 0x76, 0x8f, 0x0b, 0x84, 0x02, 0x86, 0xdf, 0x47, 0xa9, 0x96, 0x6b, + 0x89, 0xc3, 0x9f, 0xd4, 0x8b, 0x66, 0xc5, 0xf3, 0x5c, 0x6f, 0xc1, 0xb5, 0xe4, 0xb5, 0x83, 0x93, + 0x94, 0x03, 0x2e, 0x10, 0x0a, 0x18, 0xf9, 0x43, 0x02, 0xe5, 0xca, 0xee, 0xb6, 0xd3, 0x71, 0x4d, + 0x6b, 0xc5, 0x73, 0xdb, 0x1e, 0xf3, 0xfd, 0xef, 0x74, 0xf7, 0x6f, 0xa2, 0x91, 0x4d, 0xf8, 0x72, + 0x10, 0xde, 0xfe, 0xaf, 0xc6, 0xaf, 0x41, 0x87, 0x1f, 0x22, 0x3e, 0x33, 0x44, 0x1f, 0x1a, 0xa5, + 0xb1, 0xf2, 0x2f, 0x64, 0x42, 0x43, 0x05, 0xf9, 0x7d, 0x12, 0x15, 0x4e, 0x76, 0x84, 0xbb, 0x68, + 0x4c, 0x30, 0x9b, 0xda, 0xdf, 0x04, 0x66, 0x4e, 0xb3, 0x06, 0xb8, 0x9c, 0xc1, 0xa5, 0x60, 0x53, + 0xc9, 0xea, 0x52, 0x10, 0x41, 0x84, 0x6a, 0xfa, 0xe7, 0xfa, 0x4e, 0xa9, 0x5d, 0xe5, 0x93, 0xdf, + 0xff, 0x2a, 0xdf, 0x40, 0x13, 0x22, 0x44, 0xc3, 0x0f, 0xca, 0xa9, 0x62, 0x72, 0x26, 0x5d, 0xba, + 0xc6, 0xab, 0xed, 0x9a, 0x18, 0x56, 0xc3, 0x4f, 0xc9, 0x53, 0x51, 0xb0, 0x0a, 0x30, 0x8c, 0xb6, + 0xdc, 0x10, 0x8d, 0x71, 0xf1, 0x62, 0xec, 0xa6, 0x27, 0x52, 0xfd, 0x07, 0xa7, 0xbc, 0xd9, 0x69, + 0x37, 0x39, 0x92, 0x41, 0xa9, 0x15, 0xdb, 0x69, 0x93, 0x1b, 0x28, 0xbd, 0xd0, 0x71, 0x7d, 0xa8, + 0x38, 0x1e, 0x33, 0x7d, 0xd7, 0xd1, 0x43, 0x49, 0x20, 0xea, 0xa8, 0x85, 0x48, 0xa8, 0xc4, 0x67, + 0xbf, 0x4a, 0xa2, 0x31, 0xed, 0x4f, 0x38, 0xf8, 0x27, 0xe8, 0xd2, 0x9d, 0x4a, 0xa3, 0x31, 0xbf, + 0x54, 0x69, 0xae, 0x3e, 0x58, 0xa9, 0x34, 0x17, 0x96, 0xef, 0x36, 0x56, 0x2b, 0xb4, 0xb9, 0x50, + 0xaf, 0x2d, 0x56, 0x97, 0x72, 0x43, 0x85, 0xcb, 0x7b, 0xfb, 0xc5, 0xbc, 0x66, 0x11, 0xff, 0x5b, + 0xcb, 0x0f, 0x11, 0x8e, 0x99, 0x57, 0x6b, 0xe5, 0xca, 0x47, 0xb9, 0x44, 0xe1, 0xdc, 0xde, 0x7e, + 0x31, 0xa7, 0x59, 0x89, 0x4f, 0x70, 0x3f, 0x46, 0x2f, 0x1c, 0x65, 0x37, 0xef, 0xae, 0x94, 0xe7, + 0x57, 0x2b, 0xb9, 0xe1, 0x42, 0x61, 0x6f, 0xbf, 0x78, 0xe1, 0xb0, 0x91, 0x0c, 0xc1, 0xd7, 0xd0, + 0xb9, 0x98, 0x29, 0xad, 0x7c, 0x78, 0xb7, 0xd2, 0x58, 0xcd, 0x25, 0x0b, 0x17, 0xf6, 0xf6, 0x8b, + 0x58, 0xb3, 0x0a, 0xdb, 0xc4, 0x1c, 0x3a, 0x7f, 0xc8, 0xa2, 0xb1, 0x52, 0xaf, 0x35, 0x2a, 0xb9, + 0x54, 0xe1, 0xe2, 0xde, 0x7e, 0xf1, 0x6c, 0xcc, 0x44, 0x56, 0x95, 0x05, 0x34, 0x1d, 0xb3, 0x29, + 0xd7, 0xef, 0xd7, 0x96, 0xeb, 0xf3, 0xe5, 0xe6, 0x0a, 0xad, 0x2f, 0xd1, 0x4a, 0xa3, 0x91, 0x4b, + 0x17, 0x8c, 0xbd, 0xfd, 0xe2, 0x25, 0xcd, 0xf8, 0x48, 0x86, 0xcf, 0xa2, 0xa9, 0x98, 0x93, 0x95, + 0x6a, 0x6d, 0x29, 0x97, 0x29, 0x9c, 0xdd, 0xdb, 0x2f, 0x9e, 0xd1, 0xec, 0xf8, 0x59, 0x1e, 0xd9, + 0xbf, 0x85, 0xe5, 0x7a, 0xa3, 0x92, 0x1b, 0x39, 0xb2, 0x7f, 0x70, 0xe0, 0xb3, 0xbf, 0x4b, 0x20, + 0x7c, 0xf4, 0xaf, 0x66, 0xf8, 0x1d, 0x94, 0x0f, 0x9d, 0x2c, 0xd4, 0xef, 0xac, 0xf0, 0x75, 0x56, + 0xeb, 0xb5, 0x66, 0xad, 0x5e, 0xab, 0xe4, 0x86, 0x62, 0xbb, 0xaa, 0x59, 0xd5, 0x5c, 0x87, 0xe1, + 0x3a, 0xba, 0x78, 0x9c, 0xe5, 0xf2, 0xc3, 0x37, 0x73, 0x89, 0xc2, 0xdc, 0xde, 0x7e, 0xf1, 0xfc, + 0x51, 0xc3, 0xe5, 0x87, 0x6f, 0x7e, 0xfd, 0xab, 0x97, 0x8e, 0x57, 0xcc, 0xf2, 0x01, 0x48, 0x5f, + 0xda, 0xeb, 0xe8, 0x9c, 0xee, 0xf8, 0x4e, 0x65, 0x75, 0xbe, 0x3c, 0xbf, 0x3a, 0x9f, 0x1b, 0x12, + 0x67, 0xa0, 0x51, 0xef, 0xb0, 0xbe, 0x09, 0x65, 0xf7, 0x15, 0x34, 0x15, 0x7b, 0x8b, 0xca, 0xbd, + 0x0a, 0x0d, 0x23, 0x4a, 0x5f, 0x3f, 0xdb, 0x62, 0x1e, 0x7e, 0x15, 0x61, 0x9d, 0x3c, 0xbf, 0x7c, + 0x7f, 0xfe, 0x41, 0x23, 0x37, 0x5c, 0x38, 0xbf, 0xb7, 0x5f, 0x9c, 0xd2, 0xd8, 0xf3, 0x9d, 0x6d, + 0x73, 0xd7, 0x9f, 0xfd, 0xf3, 0x30, 0x1a, 0xd7, 0xbf, 0x1b, 0xe1, 0x57, 0xd1, 0xd9, 0xc5, 0xea, + 0x32, 0x8f, 0xc4, 0xc5, 0xba, 0x38, 0x01, 0x2e, 0xe6, 0x86, 0xc4, 0xe3, 0x74, 0x2a, 0xff, 0x8d, + 0x7f, 0x84, 0xf2, 0x87, 0xe8, 0xe5, 0x2a, 0xad, 0x2c, 0xac, 0xd6, 0xe9, 0x83, 0x5c, 0xa2, 0xf0, + 0x02, 0xdf, 0x30, 0xdd, 0xa6, 0x6c, 0x7b, 0x50, 0x82, 0x76, 0xf1, 0x4d, 0x74, 0xe9, 0x90, 0x61, + 0xe3, 0xc1, 0x9d, 0xe5, 0x6a, 0xed, 0xb6, 0x78, 0xde, 0x70, 0xe1, 0xca, 0xde, 0x7e, 0xf1, 0xa2, + 0x6e, 0xdb, 0x10, 0x9f, 0xe2, 0x38, 0x94, 0x4d, 0xe0, 0x5b, 0xa8, 0x78, 0x82, 0x7d, 0xb4, 0x80, + 0x64, 0x81, 0xec, 0xed, 0x17, 0x2f, 0x1f, 0xe3, 0x44, 0xad, 0x23, 0x9b, 0xc0, 0x6f, 0xa0, 0x0b, + 0xc7, 0x7b, 0x0a, 0xf3, 0xe2, 0x18, 0xfb, 0xd9, 0xbf, 0x26, 0xd0, 0xa8, 0xea, 0x7a, 0x7c, 0xd3, + 0x2a, 0x94, 0xd6, 0x79, 0x91, 0x28, 0x57, 0x9a, 0xb5, 0x7a, 0x13, 0xa4, 0x70, 0xd3, 0x14, 0xaf, + 0xe6, 0xc2, 0x4f, 0x1e, 0xe3, 0x1a, 0x7d, 0xa9, 0x52, 0xab, 0xd0, 0xea, 0x42, 0x78, 0xa2, 0x8a, + 0xbd, 0xc4, 0x1c, 0xe6, 0xd9, 0x2d, 0xfc, 0x26, 0xba, 0x18, 0x77, 0xde, 0xb8, 0xbb, 0x70, 0x2b, + 0xdc, 0x25, 0x58, 0xa0, 0xf6, 0x80, 0xc6, 0x66, 0x6b, 0x03, 0x0e, 0xe6, 0xad, 0x98, 0x55, 0xb5, + 0x76, 0x6f, 0x7e, 0xb9, 0x5a, 0x16, 0x56, 0xc9, 0x42, 0x7e, 0x6f, 0xbf, 0x78, 0x4e, 0x59, 0xc9, + 0x0f, 0x1c, 0xdc, 0x6c, 0xf6, 0xeb, 0x04, 0x9a, 0xfe, 0xf6, 0xe6, 0x85, 0xef, 0xa3, 0x97, 0x61, + 0xbf, 0x8e, 0x94, 0x02, 0x59, 0xb7, 0xc4, 0x1e, 0xce, 0xaf, 0xac, 0x54, 0x6a, 0xe5, 0xdc, 0x50, + 0x61, 0x66, 0x6f, 0xbf, 0x78, 0xf5, 0xdb, 0x5d, 0xce, 0xf7, 0x7a, 0xcc, 0xb1, 0x4e, 0xe9, 0x78, + 0xb1, 0x4e, 0x97, 0x2a, 0xab, 0xb9, 0xc4, 0x69, 0x1c, 0x2f, 0xba, 0x5e, 0x9b, 0xf5, 0x4b, 0x77, + 0x1e, 0x7f, 0x33, 0x3d, 0xf4, 0xe4, 0x9b, 0xe9, 0xa1, 0xc7, 0x4f, 0xa7, 0x13, 0x4f, 0x9e, 0x4e, + 0x27, 0x7e, 0xfd, 0x6c, 0x7a, 0xe8, 0xcb, 0x67, 0xd3, 0x89, 0x27, 0xcf, 0xa6, 0x87, 0xfe, 0xf6, + 0x6c, 0x7a, 0xe8, 0xe1, 0x2b, 0x6d, 0xbb, 0xbf, 0xb1, 0xb9, 0x76, 0xad, 0xe5, 0x76, 0xaf, 0xfb, + 0xbb, 0x4e, 0xab, 0xbf, 0x61, 0x3b, 0x6d, 0xed, 0x97, 0xfe, 0xbf, 0x27, 0xd6, 0x32, 0xf0, 0xeb, + 0x8d, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0xa2, 0xde, 0xa7, 0x53, 0x54, 0x21, 0x00, 0x00, } func (m *Hello) Marshal() (dAtA []byte, err error) { @@ -1440,6 +1442,16 @@ func (m *ClusterConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.Secondary { + i-- + if m.Secondary { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } if len(m.Folders) > 0 { for iNdEx := len(m.Folders) - 1; iNdEx >= 0; iNdEx-- { { @@ -2642,6 +2654,9 @@ func (m *ClusterConfig) ProtoSize() (n int) { n += 1 + l + sovBep(uint64(l)) } } + if m.Secondary { + n += 2 + } return n } @@ -3430,6 +3445,26 @@ func (m *ClusterConfig) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Secondary", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBep + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Secondary = bool(v != 0) default: iNdEx = preIndex skippy, err := skipBep(dAtA[iNdEx:]) diff --git a/lib/protocol/common_test.go b/lib/protocol/common_test.go index 1d7b4d389..a8ddec8d7 100644 --- a/lib/protocol/common_test.go +++ b/lib/protocol/common_test.go @@ -13,8 +13,8 @@ type TestModel struct { hash []byte weakHash uint32 fromTemporary bool - indexFn func(DeviceID, string, []FileInfo) - ccFn func(DeviceID, ClusterConfig) + indexFn func(string, []FileInfo) + ccFn func(ClusterConfig) closedCh chan struct{} closedErr error } @@ -25,18 +25,18 @@ func newTestModel() *TestModel { } } -func (t *TestModel) Index(deviceID DeviceID, folder string, files []FileInfo) error { +func (t *TestModel) Index(_ Connection, folder string, files []FileInfo) error { if t.indexFn != nil { - t.indexFn(deviceID, folder, files) + t.indexFn(folder, files) } return nil } -func (*TestModel) IndexUpdate(_ DeviceID, _ string, _ []FileInfo) error { +func (*TestModel) IndexUpdate(Connection, string, []FileInfo) error { return nil } -func (t *TestModel) Request(_ DeviceID, folder, name string, _, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) { +func (t *TestModel) Request(_ Connection, folder, name string, _, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) { t.folder = folder t.name = name t.offset = offset @@ -49,19 +49,19 @@ func (t *TestModel) Request(_ DeviceID, folder, name string, _, size int32, offs return &fakeRequestResponse{buf}, nil } -func (t *TestModel) Closed(_ string, err error) { +func (t *TestModel) Closed(_ Connection, err error) { t.closedErr = err close(t.closedCh) } -func (t *TestModel) ClusterConfig(deviceID DeviceID, config ClusterConfig) error { +func (t *TestModel) ClusterConfig(_ Connection, config ClusterConfig) error { if t.ccFn != nil { - t.ccFn(deviceID, config) + t.ccFn(config) } return nil } -func (*TestModel) DownloadProgress(DeviceID, string, []FileDownloadProgressUpdate) error { +func (*TestModel) DownloadProgress(Connection, string, []FileDownloadProgressUpdate) error { return nil } diff --git a/lib/protocol/encryption.go b/lib/protocol/encryption.go index d43afa0ba..1c8644cbe 100644 --- a/lib/protocol/encryption.go +++ b/lib/protocol/encryption.go @@ -43,12 +43,12 @@ const ( // receives encrypted metadata and requests from the untrusted device, so it // must decrypt those and answer requests by encrypting the data. type encryptedModel struct { - model Model + model contextLessModel folderKeys *folderKeyRegistry keyGen *KeyGenerator } -func newEncryptedModel(model Model, folderKeys *folderKeyRegistry, keyGen *KeyGenerator) encryptedModel { +func newEncryptedModel(model contextLessModel, folderKeys *folderKeyRegistry, keyGen *KeyGenerator) encryptedModel { return encryptedModel{ model: model, folderKeys: folderKeys, @@ -56,30 +56,30 @@ func newEncryptedModel(model Model, folderKeys *folderKeyRegistry, keyGen *KeyGe } } -func (e encryptedModel) Index(deviceID DeviceID, folder string, files []FileInfo) error { +func (e encryptedModel) Index(folder string, files []FileInfo) error { if folderKey, ok := e.folderKeys.get(folder); ok { // incoming index data to be decrypted if err := decryptFileInfos(e.keyGen, files, folderKey); err != nil { return err } } - return e.model.Index(deviceID, folder, files) + return e.model.Index(folder, files) } -func (e encryptedModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo) error { +func (e encryptedModel) IndexUpdate(folder string, files []FileInfo) error { if folderKey, ok := e.folderKeys.get(folder); ok { // incoming index data to be decrypted if err := decryptFileInfos(e.keyGen, files, folderKey); err != nil { return err } } - return e.model.IndexUpdate(deviceID, folder, files) + return e.model.IndexUpdate(folder, files) } -func (e encryptedModel) Request(deviceID DeviceID, folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) { +func (e encryptedModel) Request(folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) { folderKey, ok := e.folderKeys.get(folder) if !ok { - return e.model.Request(deviceID, folder, name, blockNo, size, offset, hash, weakHash, fromTemporary) + return e.model.Request(folder, name, blockNo, size, offset, hash, weakHash, fromTemporary) } // Figure out the real file name, offset and size from the encrypted / @@ -120,7 +120,7 @@ func (e encryptedModel) Request(deviceID DeviceID, folder, name string, blockNo, // Perform that request and grab the data. - resp, err := e.model.Request(deviceID, folder, realName, blockNo, realSize, realOffset, realHash, 0, false) + resp, err := e.model.Request(folder, realName, blockNo, realSize, realOffset, realHash, 0, false) if err != nil { return nil, err } @@ -142,21 +142,21 @@ func (e encryptedModel) Request(deviceID DeviceID, folder, name string, blockNo, return rawResponse{enc}, nil } -func (e encryptedModel) DownloadProgress(deviceID DeviceID, folder string, updates []FileDownloadProgressUpdate) error { +func (e encryptedModel) DownloadProgress(folder string, updates []FileDownloadProgressUpdate) error { if _, ok := e.folderKeys.get(folder); !ok { - return e.model.DownloadProgress(deviceID, folder, updates) + return e.model.DownloadProgress(folder, updates) } // Encrypted devices shouldn't send these - ignore them. return nil } -func (e encryptedModel) ClusterConfig(deviceID DeviceID, config ClusterConfig) error { - return e.model.ClusterConfig(deviceID, config) +func (e encryptedModel) ClusterConfig(config ClusterConfig) error { + return e.model.ClusterConfig(config) } -func (e encryptedModel) Closed(connID string, err error) { - e.model.Closed(connID, err) +func (e encryptedModel) Closed(err error) { + e.model.Closed(err) } // The encryptedConnection sits between the model and the encrypted device. It diff --git a/lib/protocol/nativemodel_darwin.go b/lib/protocol/nativemodel_darwin.go index ef5d15b35..b51513df7 100644 --- a/lib/protocol/nativemodel_darwin.go +++ b/lib/protocol/nativemodel_darwin.go @@ -9,27 +9,27 @@ package protocol import "golang.org/x/text/unicode/norm" -func makeNative(m Model) Model { return nativeModel{m} } +func makeNative(m contextLessModel) contextLessModel { return nativeModel{m} } type nativeModel struct { - Model + contextLessModel } -func (m nativeModel) Index(deviceID DeviceID, folder string, files []FileInfo) error { +func (m nativeModel) Index(folder string, files []FileInfo) error { for i := range files { files[i].Name = norm.NFD.String(files[i].Name) } - return m.Model.Index(deviceID, folder, files) + return m.contextLessModel.Index(folder, files) } -func (m nativeModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo) error { +func (m nativeModel) IndexUpdate(folder string, files []FileInfo) error { for i := range files { files[i].Name = norm.NFD.String(files[i].Name) } - return m.Model.IndexUpdate(deviceID, folder, files) + return m.contextLessModel.IndexUpdate(folder, files) } -func (m nativeModel) Request(deviceID DeviceID, folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) { +func (m nativeModel) Request(folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) { name = norm.NFD.String(name) - return m.Model.Request(deviceID, folder, name, blockNo, size, offset, hash, weakHash, fromTemporary) + return m.contextLessModel.Request(folder, name, blockNo, size, offset, hash, weakHash, fromTemporary) } diff --git a/lib/protocol/protocol.go b/lib/protocol/protocol.go index eae9eee69..3e2ef9f3e 100644 --- a/lib/protocol/protocol.go +++ b/lib/protocol/protocol.go @@ -121,19 +121,34 @@ var ( errFileHasNoBlocks = errors.New("file with empty block list") ) +type contextLessModel interface { + // An index was received from the peer device + Index(folder string, files []FileInfo) error + // An index update was received from the peer device + IndexUpdate(folder string, files []FileInfo) error + // A request was made by the peer device + Request(folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) + // A cluster configuration message was received + ClusterConfig(config ClusterConfig) error + // The peer device closed the connection or an error occurred + Closed(err error) + // The peer device sent progress updates for the files it is currently downloading + DownloadProgress(folder string, updates []FileDownloadProgressUpdate) error +} + type Model interface { // An index was received from the peer device - Index(deviceID DeviceID, folder string, files []FileInfo) error + Index(conn Connection, folder string, files []FileInfo) error // An index update was received from the peer device - IndexUpdate(deviceID DeviceID, folder string, files []FileInfo) error + IndexUpdate(conn Connection, folder string, files []FileInfo) error // A request was made by the peer device - Request(deviceID DeviceID, folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) + Request(conn Connection, folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) // A cluster configuration message was received - ClusterConfig(deviceID DeviceID, config ClusterConfig) error + ClusterConfig(conn Connection, config ClusterConfig) error // The peer device closed the connection or an error occurred - Closed(connID string, err error) + Closed(conn Connection, err error) // The peer device sent progress updates for the files it is currently downloading - DownloadProgress(deviceID DeviceID, folder string, updates []FileDownloadProgressUpdate) error + DownloadProgress(conn Connection, folder string, updates []FileDownloadProgressUpdate) error } type RequestResponse interface { @@ -173,7 +188,7 @@ type rawConnection struct { ConnectionInfo id DeviceID - receiver Model + model contextLessModel startTime time.Time cr *countingReader @@ -230,10 +245,16 @@ const ( // Should not be modified in production code, just for testing. var CloseTimeout = 10 * time.Second -func NewConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, closer io.Closer, receiver Model, connInfo ConnectionInfo, compress Compression, passwords map[string]string, keyGen *KeyGenerator) Connection { +func NewConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, closer io.Closer, model Model, connInfo ConnectionInfo, compress Compression, passwords map[string]string, keyGen *KeyGenerator) Connection { + // We create the wrapper for the model first, as it needs to be passed + // in at the lowest level in the stack. At the end of construction, + // before returning, we add the connection to cwm so that it can be used + // by the model. + cwm := &connectionWrappingModel{model: model} + // Encryption / decryption is first (outermost) before conversion to // native path formats. - nm := makeNative(receiver) + nm := makeNative(cwm) em := newEncryptedModel(nm, newFolderKeyRegistry(keyGen, passwords), keyGen) // We do the wire format conversion first (outermost) so that the @@ -242,17 +263,18 @@ func NewConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, closer ec := newEncryptedConnection(rc, rc, em.folderKeys, keyGen) wc := wireFormatConnection{ec} + cwm.conn = wc return wc } -func newRawConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, closer io.Closer, receiver Model, connInfo ConnectionInfo, compress Compression) *rawConnection { +func newRawConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, closer io.Closer, receiver contextLessModel, connInfo ConnectionInfo, compress Compression) *rawConnection { cr := &countingReader{Reader: reader} cw := &countingWriter{Writer: writer} return &rawConnection{ ConnectionInfo: connInfo, id: deviceID, - receiver: receiver, + model: receiver, cr: cr, cw: cw, closer: closer, @@ -463,7 +485,7 @@ func (c *rawConnection) dispatcherLoop() (err error) { switch msg := msg.(type) { case *ClusterConfig: - err = c.receiver.ClusterConfig(c.id, *msg) + err = c.model.ClusterConfig(*msg) case *Index: err = c.handleIndex(*msg) @@ -478,7 +500,7 @@ func (c *rawConnection) dispatcherLoop() (err error) { c.handleResponse(*msg) case *DownloadProgress: - err = c.receiver.DownloadProgress(c.id, msg.Folder, msg.Updates) + err = c.model.DownloadProgress(msg.Folder, msg.Updates) } if err != nil { return newHandleError(err, msgContext) @@ -581,12 +603,12 @@ func (c *rawConnection) readHeader(fourByteBuf []byte) (Header, error) { func (c *rawConnection) handleIndex(im Index) error { l.Debugf("Index(%v, %v, %d file)", c.id, im.Folder, len(im.Files)) - return c.receiver.Index(c.id, im.Folder, im.Files) + return c.model.Index(im.Folder, im.Files) } func (c *rawConnection) handleIndexUpdate(im IndexUpdate) error { l.Debugf("queueing IndexUpdate(%v, %v, %d files)", c.id, im.Folder, len(im.Files)) - return c.receiver.IndexUpdate(c.id, im.Folder, im.Files) + return c.model.IndexUpdate(im.Folder, im.Files) } // checkIndexConsistency verifies a number of invariants on FileInfos received in @@ -652,7 +674,7 @@ func checkFilename(name string) error { } func (c *rawConnection) handleRequest(req Request) { - res, err := c.receiver.Request(c.id, req.Folder, req.Name, int32(req.BlockNo), int32(req.Size), req.Offset, req.Hash, req.WeakHash, req.FromTemporary) + res, err := c.model.Request(req.Folder, req.Name, int32(req.BlockNo), int32(req.Size), req.Offset, req.Hash, req.WeakHash, req.FromTemporary) if err != nil { c.send(context.Background(), &Response{ ID: req.ID, @@ -941,7 +963,7 @@ func (c *rawConnection) internalClose(err error) { <-c.dispatcherLoopStopped - c.receiver.Closed(c.ConnectionID(), err) + c.model.Closed(err) }) } @@ -1069,3 +1091,35 @@ func messageContext(msg message) (string, error) { return "", errors.New("unknown or empty message") } } + +// connectionWrappingModel takes the Model interface from the model package, +// which expects the Connection as the first parameter in all methods, and +// wraps it to conform to the protocol.Model interface. +type connectionWrappingModel struct { + conn Connection + model Model +} + +func (c *connectionWrappingModel) Index(folder string, files []FileInfo) error { + return c.model.Index(c.conn, folder, files) +} + +func (c *connectionWrappingModel) IndexUpdate(folder string, files []FileInfo) error { + return c.model.IndexUpdate(c.conn, folder, files) +} + +func (c *connectionWrappingModel) Request(folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) { + return c.model.Request(c.conn, folder, name, blockNo, size, offset, hash, weakHash, fromTemporary) +} + +func (c *connectionWrappingModel) ClusterConfig(config ClusterConfig) error { + return c.model.ClusterConfig(c.conn, config) +} + +func (c *connectionWrappingModel) Closed(err error) { + c.model.Closed(c.conn, err) +} + +func (c *connectionWrappingModel) DownloadProgress(folder string, updates []FileDownloadProgressUpdate) error { + return c.model.DownloadProgress(c.conn, folder, updates) +} diff --git a/lib/protocol/protocol_test.go b/lib/protocol/protocol_test.go index 90916f760..69f954aa3 100644 --- a/lib/protocol/protocol_test.go +++ b/lib/protocol/protocol_test.go @@ -145,7 +145,7 @@ func TestCloseRace(t *testing.T) { indexReceived := make(chan struct{}) unblockIndex := make(chan struct{}) m0 := newTestModel() - m0.indexFn = func(_ DeviceID, _ string, _ []FileInfo) { + m0.indexFn = func(string, []FileInfo) { close(indexReceived) <-unblockIndex } @@ -924,7 +924,7 @@ func TestDispatcherToCloseDeadlock(t *testing.T) { m := newTestModel() rw := testutils.NewBlockingRW() c := getRawConnection(NewConnection(c0ID, rw, &testutils.NoopRW{}, testutils.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, nil, testKeyGen)) - m.ccFn = func(devID DeviceID, cc ClusterConfig) { + m.ccFn = func(ClusterConfig) { c.Close(errManual) } c.Start() diff --git a/proto/lib/config/deviceconfiguration.proto b/proto/lib/config/deviceconfiguration.proto index 616329b76..097fb5c98 100644 --- a/proto/lib/config/deviceconfiguration.proto +++ b/proto/lib/config/deviceconfiguration.proto @@ -26,4 +26,5 @@ message DeviceConfiguration { int32 max_request_kib = 16 [(ext.goname) = "MaxRequestKiB", (ext.xml) = "maxRequestKiB", (ext.json) = "maxRequestKiB"]; bool untrusted = 17; int32 remote_gui_port = 18 [(ext.goname) = "RemoteGUIPort", (ext.xml) = "remoteGUIPort", (ext.json) = "remoteGUIPort"]; + int32 multiple_connections = 19; // if >1 and supported by the other side, attempt to establish this many connections to the device } diff --git a/proto/lib/protocol/bep.proto b/proto/lib/protocol/bep.proto index 7e76464a2..b54e89954 100644 --- a/proto/lib/protocol/bep.proto +++ b/proto/lib/protocol/bep.proto @@ -41,7 +41,8 @@ enum MessageCompression { // Cluster Config message ClusterConfig { - repeated Folder folders = 1; + repeated Folder folders = 1; + bool secondary = 2; } message Folder { diff --git a/test/h1/config.xml b/test/h1/config.xml index bc3b1a31f..1cbd6f7c2 100644 --- a/test/h1/config.xml +++ b/test/h1/config.xml @@ -1,5 +1,5 @@ - + basic @@ -49,60 +49,6 @@ 0 - - basic - - - - - - - 1 - - 3600 - - basic - - 1 - 0 - 0 - random - false - 0 - 0 - -1 - false - false - false - 25 - .stfolder - false - 0 - 2 - false - standard - standard - false - true - false - false - false - false - - 0 - 0 - - - -
tcp://127.0.0.1:22004
- false - false - 0 - 0 - 0 - false - 0 -
tcp://127.0.0.1:22001
false @@ -122,26 +68,7 @@ 0 false 0 -
- -
tcp://127.0.0.1:22003
- false - false - 0 - 0 - 0 - false - 0 -
- -
tcp://127.0.0.1:22004
- false - false - 0 - 0 - 0 - false - 0 + 4
127.0.0.1:8081
diff --git a/test/h2/config.xml b/test/h2/config.xml index 3de77b88e..ae7cc494a 100644 --- a/test/h2/config.xml +++ b/test/h2/config.xml @@ -1,5 +1,5 @@ - + basic @@ -46,94 +46,6 @@ 0 - - basic - - - - - - - 1 - - 3600 - - basic - - 1 - 0 - 0 - random - false - 0 - 0 - -1 - false - false - false - 25 - .stfolder - false - 0 - 2 - false - standard - standard - false - true - false - false - false - false - - 0 - 0 - - - - basic - - - - - - - 1 - - 3600 - - basic - - 1 - 0 - 0 - random - false - 0 - 0 - -1 - false - false - false - 25 - .stfolder - false - 0 - 2 - false - standard - standard - false - true - false - false - false - false - - 0 - 0 - -
tcp://127.0.0.1:22001
false @@ -143,6 +55,7 @@ 0 false 0 + 4
tcp://127.0.0.1:22002
@@ -154,16 +67,6 @@ false 0
- -
tcp://127.0.0.1:22003
- false - false - 0 - 0 - 0 - false - 0 -
127.0.0.1:8082
abc123