This commit is contained in:
Jakob Borg 2023-05-23 14:53:48 +02:00
parent da9726ddca
commit f19a9c49af
21 changed files with 690 additions and 664 deletions

View File

@ -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:])

View File

@ -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 {

View File

@ -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,

View File

@ -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 {

View File

@ -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)

View File

@ -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]

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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() {

View File

@ -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
}

View File

@ -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:])

View File

@ -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
}

View File

@ -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

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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()

View File

@ -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
}

View File

@ -41,7 +41,8 @@ enum MessageCompression {
// Cluster Config
message ClusterConfig {
repeated Folder folders = 1;
repeated Folder folders = 1;
bool secondary = 2;
}
message Folder {

View File

@ -1,5 +1,5 @@
<configuration version="37">
<folder id="default" label="" path="s1/" type="sendreceive" rescanIntervalS="10" fsWatcherEnabled="false" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
<folder id="default" label="" path="s1/" type="sendreceive" rescanIntervalS="3600" fsWatcherEnabled="true" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
<filesystemType>basic</filesystemType>
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" introducedBy="">
<encryptionPassword></encryptionPassword>
@ -49,60 +49,6 @@
<maxTotalSize>0</maxTotalSize>
</xattrFilter>
</folder>
<folder id="¯\_(ツ)_/¯ Räksmörgås 动作 Адрес" label="" path="s12-1/" type="sendreceive" rescanIntervalS="10" fsWatcherEnabled="false" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
<filesystemType>basic</filesystemType>
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" introducedBy="">
<encryptionPassword></encryptionPassword>
</device>
<device id="MRIW7OK-NETT3M4-N6SBWME-N25O76W-YJKVXPH-FUMQJ3S-P57B74J-GBITBAC" introducedBy="">
<encryptionPassword></encryptionPassword>
</device>
<minDiskFree unit="%">1</minDiskFree>
<versioning>
<cleanupIntervalS>3600</cleanupIntervalS>
<fsPath></fsPath>
<fsType>basic</fsType>
</versioning>
<copiers>1</copiers>
<pullerMaxPendingKiB>0</pullerMaxPendingKiB>
<hashers>0</hashers>
<order>random</order>
<ignoreDelete>false</ignoreDelete>
<scanProgressIntervalS>0</scanProgressIntervalS>
<pullerPauseS>0</pullerPauseS>
<maxConflicts>-1</maxConflicts>
<disableSparseFiles>false</disableSparseFiles>
<disableTempIndexes>false</disableTempIndexes>
<paused>false</paused>
<weakHashThresholdPct>25</weakHashThresholdPct>
<markerName>.stfolder</markerName>
<copyOwnershipFromParent>false</copyOwnershipFromParent>
<modTimeWindowS>0</modTimeWindowS>
<maxConcurrentWrites>2</maxConcurrentWrites>
<disableFsync>false</disableFsync>
<blockPullOrder>standard</blockPullOrder>
<copyRangeMethod>standard</copyRangeMethod>
<caseSensitiveFS>false</caseSensitiveFS>
<junctionsAsDirs>true</junctionsAsDirs>
<syncOwnership>false</syncOwnership>
<sendOwnership>false</sendOwnership>
<syncXattrs>false</syncXattrs>
<sendXattrs>false</sendXattrs>
<xattrFilter>
<maxSingleEntrySize>0</maxSingleEntrySize>
<maxTotalSize>0</maxTotalSize>
</xattrFilter>
</folder>
<device id="EJHMPAQ-OGCVORE-ISB4IS3-SYYVJXF-TKJGLTU-66DIQPF-GJ5D2GX-GQ3OWQK" name="s4" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
<address>tcp://127.0.0.1:22004</address>
<paused>false</paused>
<autoAcceptFolders>false</autoAcceptFolders>
<maxSendKbps>0</maxSendKbps>
<maxRecvKbps>0</maxRecvKbps>
<maxRequestKiB>0</maxRequestKiB>
<untrusted>false</untrusted>
<remoteGUIPort>0</remoteGUIPort>
</device>
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" name="s1" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
<address>tcp://127.0.0.1:22001</address>
<paused>false</paused>
@ -122,26 +68,7 @@
<maxRequestKiB>0</maxRequestKiB>
<untrusted>false</untrusted>
<remoteGUIPort>0</remoteGUIPort>
</device>
<device id="373HSRP-QLPNLIE-JYKZVQF-P4PKZ63-R2ZE6K3-YD442U2-JHBGBQG-WWXAHAU" name="s3" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
<address>tcp://127.0.0.1:22003</address>
<paused>false</paused>
<autoAcceptFolders>false</autoAcceptFolders>
<maxSendKbps>0</maxSendKbps>
<maxRecvKbps>0</maxRecvKbps>
<maxRequestKiB>0</maxRequestKiB>
<untrusted>false</untrusted>
<remoteGUIPort>0</remoteGUIPort>
</device>
<device id="7PBCTLL-JJRYBSA-MOWZRKL-MSDMN4N-4US4OMX-SYEXUS4-HSBGNRY-CZXRXAT" name="s4" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
<address>tcp://127.0.0.1:22004</address>
<paused>false</paused>
<autoAcceptFolders>false</autoAcceptFolders>
<maxSendKbps>0</maxSendKbps>
<maxRecvKbps>0</maxRecvKbps>
<maxRequestKiB>0</maxRequestKiB>
<untrusted>false</untrusted>
<remoteGUIPort>0</remoteGUIPort>
<multipleConnections>4</multipleConnections>
</device>
<gui enabled="true" tls="false" debugging="true">
<address>127.0.0.1:8081</address>

View File

@ -1,5 +1,5 @@
<configuration version="37">
<folder id="default" label="" path="s2" type="sendreceive" rescanIntervalS="15" fsWatcherEnabled="false" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
<folder id="default" label="" path="s2" type="sendreceive" rescanIntervalS="3600" fsWatcherEnabled="true" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
<filesystemType>basic</filesystemType>
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" introducedBy="">
<encryptionPassword></encryptionPassword>
@ -46,94 +46,6 @@
<maxTotalSize>0</maxTotalSize>
</xattrFilter>
</folder>
<folder id="s23" label="" path="s23-2" type="sendreceive" rescanIntervalS="15" fsWatcherEnabled="false" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
<filesystemType>basic</filesystemType>
<device id="MRIW7OK-NETT3M4-N6SBWME-N25O76W-YJKVXPH-FUMQJ3S-P57B74J-GBITBAC" introducedBy="">
<encryptionPassword></encryptionPassword>
</device>
<device id="373HSRP-QLPNLIE-JYKZVQF-P4PKZ63-R2ZE6K3-YD442U2-JHBGBQG-WWXAHAU" introducedBy="">
<encryptionPassword></encryptionPassword>
</device>
<minDiskFree unit="%">1</minDiskFree>
<versioning>
<cleanupIntervalS>3600</cleanupIntervalS>
<fsPath></fsPath>
<fsType>basic</fsType>
</versioning>
<copiers>1</copiers>
<pullerMaxPendingKiB>0</pullerMaxPendingKiB>
<hashers>0</hashers>
<order>random</order>
<ignoreDelete>false</ignoreDelete>
<scanProgressIntervalS>0</scanProgressIntervalS>
<pullerPauseS>0</pullerPauseS>
<maxConflicts>-1</maxConflicts>
<disableSparseFiles>false</disableSparseFiles>
<disableTempIndexes>false</disableTempIndexes>
<paused>false</paused>
<weakHashThresholdPct>25</weakHashThresholdPct>
<markerName>.stfolder</markerName>
<copyOwnershipFromParent>false</copyOwnershipFromParent>
<modTimeWindowS>0</modTimeWindowS>
<maxConcurrentWrites>2</maxConcurrentWrites>
<disableFsync>false</disableFsync>
<blockPullOrder>standard</blockPullOrder>
<copyRangeMethod>standard</copyRangeMethod>
<caseSensitiveFS>false</caseSensitiveFS>
<junctionsAsDirs>true</junctionsAsDirs>
<syncOwnership>false</syncOwnership>
<sendOwnership>false</sendOwnership>
<syncXattrs>false</syncXattrs>
<sendXattrs>false</sendXattrs>
<xattrFilter>
<maxSingleEntrySize>0</maxSingleEntrySize>
<maxTotalSize>0</maxTotalSize>
</xattrFilter>
</folder>
<folder id="¯\_(ツ)_/¯ Räksmörgås 动作 Адрес" label="" path="s12-2" type="sendreceive" rescanIntervalS="15" fsWatcherEnabled="false" fsWatcherDelayS="10" ignorePerms="false" autoNormalize="true">
<filesystemType>basic</filesystemType>
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" introducedBy="">
<encryptionPassword></encryptionPassword>
</device>
<device id="MRIW7OK-NETT3M4-N6SBWME-N25O76W-YJKVXPH-FUMQJ3S-P57B74J-GBITBAC" introducedBy="">
<encryptionPassword></encryptionPassword>
</device>
<minDiskFree unit="%">1</minDiskFree>
<versioning>
<cleanupIntervalS>3600</cleanupIntervalS>
<fsPath></fsPath>
<fsType>basic</fsType>
</versioning>
<copiers>1</copiers>
<pullerMaxPendingKiB>0</pullerMaxPendingKiB>
<hashers>0</hashers>
<order>random</order>
<ignoreDelete>false</ignoreDelete>
<scanProgressIntervalS>0</scanProgressIntervalS>
<pullerPauseS>0</pullerPauseS>
<maxConflicts>-1</maxConflicts>
<disableSparseFiles>false</disableSparseFiles>
<disableTempIndexes>false</disableTempIndexes>
<paused>false</paused>
<weakHashThresholdPct>25</weakHashThresholdPct>
<markerName>.stfolder</markerName>
<copyOwnershipFromParent>false</copyOwnershipFromParent>
<modTimeWindowS>0</modTimeWindowS>
<maxConcurrentWrites>2</maxConcurrentWrites>
<disableFsync>false</disableFsync>
<blockPullOrder>standard</blockPullOrder>
<copyRangeMethod>standard</copyRangeMethod>
<caseSensitiveFS>false</caseSensitiveFS>
<junctionsAsDirs>true</junctionsAsDirs>
<syncOwnership>false</syncOwnership>
<sendOwnership>false</sendOwnership>
<syncXattrs>false</syncXattrs>
<sendXattrs>false</sendXattrs>
<xattrFilter>
<maxSingleEntrySize>0</maxSingleEntrySize>
<maxTotalSize>0</maxTotalSize>
</xattrFilter>
</folder>
<device id="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU" name="s1" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
<address>tcp://127.0.0.1:22001</address>
<paused>false</paused>
@ -143,6 +55,7 @@
<maxRequestKiB>0</maxRequestKiB>
<untrusted>false</untrusted>
<remoteGUIPort>0</remoteGUIPort>
<multipleConnections>4</multipleConnections>
</device>
<device id="MRIW7OK-NETT3M4-N6SBWME-N25O76W-YJKVXPH-FUMQJ3S-P57B74J-GBITBAC" name="s2" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
<address>tcp://127.0.0.1:22002</address>
@ -154,16 +67,6 @@
<untrusted>false</untrusted>
<remoteGUIPort>0</remoteGUIPort>
</device>
<device id="373HSRP-QLPNLIE-JYKZVQF-P4PKZ63-R2ZE6K3-YD442U2-JHBGBQG-WWXAHAU" name="s3" compression="metadata" introducer="false" skipIntroductionRemovals="false" introducedBy="">
<address>tcp://127.0.0.1:22003</address>
<paused>false</paused>
<autoAcceptFolders>false</autoAcceptFolders>
<maxSendKbps>0</maxSendKbps>
<maxRecvKbps>0</maxRecvKbps>
<maxRequestKiB>0</maxRequestKiB>
<untrusted>false</untrusted>
<remoteGUIPort>0</remoteGUIPort>
</device>
<gui enabled="true" tls="false" debugging="true">
<address>127.0.0.1:8082</address>
<apikey>abc123</apikey>