#include #include #include #include #include #include #include #include #include static const BYTE testRdpFileUTF16[] = { 0xff, 0xfe, 0x73, 0x00, 0x63, 0x00, 0x72, 0x00, 0x65, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x20, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x65, 0x00, 0x20, 0x00, 0x69, 0x00, 0x64, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x32, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x75, 0x00, 0x73, 0x00, 0x65, 0x00, 0x20, 0x00, 0x6d, 0x00, 0x75, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x64, 0x00, 0x65, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x70, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x74, 0x00, 0x68, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x39, 0x00, 0x32, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x64, 0x00, 0x65, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x70, 0x00, 0x68, 0x00, 0x65, 0x00, 0x69, 0x00, 0x67, 0x00, 0x68, 0x00, 0x74, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x30, 0x00, 0x38, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x73, 0x00, 0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x20, 0x00, 0x62, 0x00, 0x70, 0x00, 0x70, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x77, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x3a, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x2c, 0x00, 0x31, 0x00, 0x2c, 0x00, 0x35, 0x00, 0x35, 0x00, 0x33, 0x00, 0x2c, 0x00, 0x32, 0x00, 0x31, 0x00, 0x31, 0x00, 0x2c, 0x00, 0x31, 0x00, 0x33, 0x00, 0x35, 0x00, 0x33, 0x00, 0x2c, 0x00, 0x38, 0x00, 0x31, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x6b, 0x00, 0x65, 0x00, 0x79, 0x00, 0x62, 0x00, 0x6f, 0x00, 0x61, 0x00, 0x72, 0x00, 0x64, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x6b, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x32, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x61, 0x00, 0x75, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x61, 0x00, 0x70, 0x00, 0x74, 0x00, 0x75, 0x00, 0x72, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x65, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x76, 0x00, 0x69, 0x00, 0x64, 0x00, 0x65, 0x00, 0x6f, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x62, 0x00, 0x61, 0x00, 0x63, 0x00, 0x6b, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x65, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x20, 0x00, 0x74, 0x00, 0x79, 0x00, 0x70, 0x00, 0x65, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x37, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x74, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6b, 0x00, 0x61, 0x00, 0x75, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x65, 0x00, 0x74, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x62, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x74, 0x00, 0x68, 0x00, 0x61, 0x00, 0x75, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x65, 0x00, 0x74, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x64, 0x00, 0x69, 0x00, 0x73, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x72, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6b, 0x00, 0x73, 0x00, 0x70, 0x00, 0x61, 0x00, 0x63, 0x00, 0x65, 0x00, 0x72, 0x00, 0x65, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x64, 0x00, 0x69, 0x00, 0x73, 0x00, 0x61, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x20, 0x00, 0x77, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x70, 0x00, 0x61, 0x00, 0x70, 0x00, 0x65, 0x00, 0x72, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x77, 0x00, 0x20, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x20, 0x00, 0x73, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x68, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x77, 0x00, 0x20, 0x00, 0x64, 0x00, 0x65, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x70, 0x00, 0x20, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x69, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x64, 0x00, 0x69, 0x00, 0x73, 0x00, 0x61, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x20, 0x00, 0x66, 0x00, 0x75, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x77, 0x00, 0x20, 0x00, 0x64, 0x00, 0x72, 0x00, 0x61, 0x00, 0x67, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x64, 0x00, 0x69, 0x00, 0x73, 0x00, 0x61, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x20, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x20, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x64, 0x00, 0x69, 0x00, 0x73, 0x00, 0x61, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x20, 0x00, 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x64, 0x00, 0x69, 0x00, 0x73, 0x00, 0x61, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x20, 0x00, 0x63, 0x00, 0x75, 0x00, 0x72, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x20, 0x00, 0x73, 0x00, 0x65, 0x00, 0x74, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x62, 0x00, 0x69, 0x00, 0x74, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x70, 0x00, 0x63, 0x00, 0x61, 0x00, 0x63, 0x00, 0x68, 0x00, 0x65, 0x00, 0x70, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x66, 0x00, 0x75, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x61, 0x00, 0x64, 0x00, 0x64, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x4c, 0x00, 0x41, 0x00, 0x42, 0x00, 0x31, 0x00, 0x2d, 0x00, 0x57, 0x00, 0x37, 0x00, 0x2d, 0x00, 0x44, 0x00, 0x4d, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x62, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x77, 0x00, 0x61, 0x00, 0x6b, 0x00, 0x65, 0x00, 0x2e, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x61, 0x00, 0x75, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x65, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x69, 0x00, 0x72, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x70, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x69, 0x00, 0x72, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x74, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x69, 0x00, 0x72, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x73, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x72, 0x00, 0x74, 0x00, 0x63, 0x00, 0x61, 0x00, 0x72, 0x00, 0x64, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x69, 0x00, 0x72, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x63, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x70, 0x00, 0x62, 0x00, 0x6f, 0x00, 0x61, 0x00, 0x72, 0x00, 0x64, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x69, 0x00, 0x72, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x64, 0x00, 0x65, 0x00, 0x76, 0x00, 0x69, 0x00, 0x63, 0x00, 0x65, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x61, 0x00, 0x75, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x65, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x20, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x64, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x61, 0x00, 0x75, 0x00, 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x69, 0x00, 0x63, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x20, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x76, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x32, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x74, 0x00, 0x20, 0x00, 0x66, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x20, 0x00, 0x63, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x67, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x69, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x73, 0x00, 0x65, 0x00, 0x63, 0x00, 0x75, 0x00, 0x72, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, 0x20, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x65, 0x00, 0x72, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x72, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x65, 0x00, 0x61, 0x00, 0x70, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x63, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x65, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x73, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x3a, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x73, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6b, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x20, 0x00, 0x64, 0x00, 0x69, 0x00, 0x72, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x79, 0x00, 0x3a, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x67, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x77, 0x00, 0x61, 0x00, 0x79, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x74, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x3a, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x4c, 0x00, 0x41, 0x00, 0x42, 0x00, 0x31, 0x00, 0x2d, 0x00, 0x57, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x38, 0x00, 0x52, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x47, 0x00, 0x57, 0x00, 0x2e, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x62, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x77, 0x00, 0x61, 0x00, 0x6b, 0x00, 0x65, 0x00, 0x2e, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x67, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x77, 0x00, 0x61, 0x00, 0x79, 0x00, 0x75, 0x00, 0x73, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x74, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x67, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x77, 0x00, 0x61, 0x00, 0x79, 0x00, 0x63, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x73, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x72, 0x00, 0x63, 0x00, 0x65, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x67, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x77, 0x00, 0x61, 0x00, 0x79, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x75, 0x00, 0x73, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x74, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x74, 0x00, 0x63, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x65, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x75, 0x00, 0x73, 0x00, 0x65, 0x00, 0x20, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x69, 0x00, 0x72, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x20, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x72, 0x00, 0x64, 0x00, 0x67, 0x00, 0x69, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x64, 0x00, 0x63, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x78, 0x00, 0x79, 0x00, 0x3a, 0x00, 0x69, 0x00, 0x3a, 0x00, 0x30, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x6b, 0x00, 0x64, 0x00, 0x63, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x78, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x3a, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x64, 0x00, 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x65, 0x00, 0x64, 0x00, 0x69, 0x00, 0x72, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x3a, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x2a, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x75, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x3a, 0x00, 0x73, 0x00, 0x3a, 0x00, 0x4c, 0x00, 0x41, 0x00, 0x42, 0x00, 0x31, 0x00, 0x5c, 0x00, 0x4a, 0x00, 0x6f, 0x00, 0x68, 0x00, 0x6e, 0x00, 0x44, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x0d, 0x00, 0x0a, 0x00 }; #if defined(CHANNEL_RDPECAM_CLIENT) static const char* camera_args[] = { RDPECAM_DVC_CHANNEL_NAME, "device:*", "device:\\?\\usb#vid_0bda&pid_58b0&mi", "device:-\\?\\usb#vid_0bdc&pid_58b1&mi", "encode:1", "quality:2" }; #endif #if defined(CHANNEL_URBDRC_CLIENT) static const char* urbdrc_args[] = { "urbdrc", "device:*", "device:USBInstanceID:someid", "device:{72631e54-78a4-11d0-bcf7-00aa00b7b32a}" }; #endif static char testRdpFileUTF8[] = "screen mode id:i:2\n" "use multimon:i:0\n" "desktopwidth:i:1920\n" "desktopheight:i:1080\n" "dynamic resolution:i:1080\n" "desktopscalefactor:i:1080\n" "redirected video capture encoding quality:i:2\n" "encode redirected video capture:i:1\n" "camerastoredirect:s:*,\\?\\usb#vid_0bda&pid_58b0&mi,-\\?\\usb#vid_0bdc&pid_58b1&mi\n" "usbdevicestoredirect:s:*,USBInstanceID:someid,{72631e54-78a4-11d0-bcf7-00aa00b7b32a}\n" "selectedmonitors:s:3,2,42,23" "session bpp:i:32\n" "winposstr:s:0,1,553,211,1353,811\n" "compression:i:1\n" "keyboardhook:i:2\n" "audiocapturemode:i:0\n" "videoplaybackmode:i:2\n" "connection type:i:7\n" "networkautodetect:i:1\n" "bandwidthautodetect:i:1\n" "displayconnectionbar:i:1\n" "enableworkspacereconnect:i:0\n" "disable wallpaper:i:0\n" "allow font smoothing:i:0\n" "allow desktop composition:i:0\n" "disable full window drag:i:1\n" "disable menu anims:i:1\n" "disable themes:i:0\n" "disable cursor setting:i:0\n" "bitmapcachepersistenable:i:1\n" "full address:s:LAB1-W7-DM-01.lab1.awake.local\n" "alternate full address:s:LAB1-W7-DM-01.lab1.awake.global\n" "audiomode:i:0\n" "redirectprinters:i:1\n" "redirectcomports:i:0\n" "redirectsmartcards:i:1\n" "redirectclipboard:i:1\n" "redirectposdevices:i:0\n" "autoreconnection enabled:i:1\n" "authentication level:i:2\n" "prompt for credentials:i:0\n" "negotiate security layer:i:1\n" "remoteapplicationmode:i:0\n" "alternate shell:s:\n" "shell working directory:s:\n" "gatewayhostname:s:LAB1-W2K8R2-GW.lab1.awake.local\n" "gatewayusagemethod:i:1\n" "gatewaycredentialssource:i:0\n" "gatewayprofileusagemethod:i:1\n" "promptcredentialonce:i:1\n" "use redirection server name:i:0\n" "rdgiskdcproxy:i:0\n" "kdcproxyname:s:\n" "drivestoredirect:s:*\n" "username:s:LAB1\\JohnDoe\n" "vendor integer:i:123\n" "vendor string:s:microsoft\n"; static char* append(const char* fmt, ...) { int rc = 0; char* dst = nullptr; va_list ap = WINPR_C_ARRAY_INIT; va_start(ap, fmt); rc = vsnprintf(nullptr, 0, fmt, ap); va_end(ap); if (rc < 0) return nullptr; dst = malloc((size_t)rc + 1); if (!dst) return nullptr; va_start(ap, fmt); rc = vsnprintf(dst, (size_t)rc + 1, fmt, ap); va_end(ap); if (rc < 0) { free(dst); return nullptr; } return dst; } static FILE* test_fopen(const char* name, const char* mode) { #ifndef TEST_SOURCE_DIR #error "TEST_SOURCE_DIR must be defined to the test source directory" #endif char* path = GetCombinedPath(TEST_SOURCE_DIR, name); FILE* fp = winpr_fopen(path, mode); free(path); return fp; } static void* read_rdp_data(const char* name, size_t* plen) { BOOL success = FALSE; char* json = nullptr; FILE* fp = test_fopen(name, "r"); if (!fp) goto fail; if (fseek(fp, 0, SEEK_END) != 0) goto fail; const INT64 pos = _ftelli64(fp); if (pos < 0) goto fail; if (fseek(fp, 0, SEEK_SET) != 0) goto fail; const size_t upos = WINPR_CXX_COMPAT_CAST(size_t, pos); json = calloc(1ULL + upos, sizeof(char)); if (!json) goto fail; if (fread(json, 1, pos, fp) != pos) goto fail; *plen = upos; success = TRUE; fail: if (!success) { char buffer[128] = WINPR_C_ARRAY_INIT; WLog_ERR(__func__, "failed to read data from '%s': %s", name, winpr_strerror(errno, buffer, sizeof(buffer))); free(json); json = nullptr; } if (fp) (void)fclose(fp); return json; } static bool save_settings(const rdpSettings* settings, const char* name) { bool rc = false; size_t datalen = 0; char* data = freerdp_settings_serialize(settings, TRUE, &datalen); if (!data) return false; FILE* fp = test_fopen(name, "w"); if (fp) { const size_t res = fwrite(data, 1, datalen, fp); (void)fclose(fp); rc = res == datalen; } free(data); return rc; } static char* get_json_name(const char* base, bool unchecked) { size_t namelen = 0; char* name = nullptr; winpr_asprintf(&name, &namelen, "%s%s.json", base, unchecked ? ".unchecked" : ""); return name; } static rdpSettings* read_json(const char* name) { size_t datalen = 0; void* data = read_rdp_data(name, &datalen); rdpSettings* settings = freerdp_settings_deserialize(data, datalen); fail: free(data); return settings; } static rdpSettings* load_from(const void* data, size_t len, bool unchecked) { BOOL rc = false; rdpFile* file = freerdp_client_rdp_file_new(); rdpSettings* settings = read_json("default-settings.json"); if (!settings) { settings = freerdp_settings_new(0); if (settings) { save_settings(settings, "default-settings.json"); } } if (!file || !settings) goto fail; if (!freerdp_client_parse_rdp_file_buffer(file, data, len)) goto fail; if (unchecked) { if (!freerdp_client_populate_settings_from_rdp_file_unchecked(file, settings)) goto fail; } else { if (!freerdp_client_populate_settings_from_rdp_file(file, settings)) goto fail; } rc = true; fail: freerdp_client_rdp_file_free(file); if (!rc) { freerdp_settings_free(settings); return nullptr; } return settings; } static rdpSettings* load_from_file(const char* name, bool unchecked) { size_t datalen = 0; void* data = read_rdp_data(name, &datalen); if (!data) return nullptr; rdpSettings* settings = load_from(data, datalen, unchecked); free(data); return settings; } static bool test_data(const char* json, const void* data, size_t len, bool unchecked) { bool rc = false; rdpSettings* settings = load_from(data, len, unchecked); rdpSettings* expect = read_json(json); if (!settings || !expect) goto fail; #ifndef WITH_GFX_H264 if (!freerdp_settings_set_bool(expect, FreeRDP_GfxH264, FALSE) || !freerdp_settings_set_bool(expect, FreeRDP_GfxAVC444, FALSE) || !freerdp_settings_set_bool(expect, FreeRDP_GfxAVC444v2, FALSE)) goto fail; #endif wLog* log = WLog_Get(__func__); WLog_Print(log, WLOG_INFO, "Test cast '%s'", json); if (freerdp_settings_print_diff(log, WLOG_ERROR, expect, settings)) goto fail; rc = true; fail: freerdp_settings_free(settings); freerdp_settings_free(expect); return rc; } static HANDLE FindFirstFileUTF8(LPCSTR pszSearchPath, WIN32_FIND_DATAW* FindData) { HANDLE hdl = INVALID_HANDLE_VALUE; if (!pszSearchPath) return hdl; WCHAR* wpath = ConvertUtf8ToWCharAlloc(pszSearchPath, nullptr); if (!wpath) return hdl; hdl = FindFirstFileW(wpath, FindData); free(wpath); return hdl; } static bool test_rdp_file(const char* base, bool allowCreate, bool unchecked) { bool rc = false; size_t rdplen = 0; char* rdp = nullptr; winpr_asprintf(&rdp, &rdplen, "%s.rdp", base); char* json = get_json_name(base, unchecked); size_t datalen = 0; void* data = read_rdp_data(rdp, &datalen); if (!data) goto fail; rc = test_data(json, data, datalen, unchecked); if (!rc && allowCreate) { rdpSettings* expect = read_json(json); if (expect) { freerdp_settings_free(expect); goto fail; } rdpSettings* settings = load_from_file(rdp, unchecked); if (!settings) goto fail; rc = save_settings(settings, json); } fail: free(data); free(json); free(rdp); return rc; } static bool test_rdp_files(bool allowCreate) { bool rc = false; /* Load RDP files from directory, compare to JSON in same directory. * If JSON does not exist, create it. */ #ifndef TEST_SOURCE_DIR #error "TEST_SOURCE_DIR must be defined to the test source directory" #endif HANDLE hdl = INVALID_HANDLE_VALUE; char* path = GetCombinedPath(TEST_SOURCE_DIR, "rdp-testcases/*.rdp"); if (!path) goto fail; WIN32_FIND_DATAW FindData = WINPR_C_ARRAY_INIT; hdl = FindFirstFileUTF8(path, &FindData); if (hdl == INVALID_HANDLE_VALUE) { WLog_INFO( __func__, "no RDP files found in %s. Add RDP files to generate settings JSON for comparison.", path); rc = true; goto fail; } do { if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { char cFileName[6 * MAX_PATH] = WINPR_C_ARRAY_INIT; char rdp[6 * MAX_PATH] = WINPR_C_ARRAY_INIT; ConvertWCharToUtf8(FindData.cFileName, cFileName, sizeof(cFileName)); const size_t len = strnlen(cFileName, sizeof(cFileName)); if (len < 4) continue; strcpy(rdp, "rdp-testcases/"); strncpy(&rdp[14], cFileName, len - 4); if (!test_rdp_file(rdp, allowCreate, false)) goto fail; if (!test_rdp_file(rdp, allowCreate, true)) goto fail; } } while (FindNextFileW(hdl, &FindData)); rc = true; fail: FindClose(hdl); free(path); return rc; } int TestClientRdpFile(int argc, char* argv[]) { int rc = -1; int iValue = 0; UINT32 uValue = 0; const UINT32* puValue = nullptr; const char* sValue = nullptr; char* utfname = nullptr; char* uniname = nullptr; char* base = nullptr; char* tmp = nullptr; UINT64 id = 0; rdpFile* file = nullptr; rdpSettings* settings = nullptr; WINPR_UNUSED(argc); WINPR_UNUSED(argv); if (winpr_RAND(&id, sizeof(id)) < 0) return -1; /* UTF8 */ #if defined(CHANNEL_URBDRC_CLIENT) && defined(CHANNEL_RDPECAM_CLIENT) if (!test_data("testRdpFileUTF8.json", testRdpFileUTF8, sizeof(testRdpFileUTF8), false)) return -1; if (!test_data("testRdpFileUTF8.unchecked.json", testRdpFileUTF8, sizeof(testRdpFileUTF8), true)) return -1; #endif /* Unicode */ #if defined(CHANNEL_URBDRC_CLIENT) if (!test_data("testRdpFileUTF16.json", testRdpFileUTF16, sizeof(testRdpFileUTF16), false)) return -1; if (!test_data("testRdpFileUTF16.unchecked.json", testRdpFileUTF16, sizeof(testRdpFileUTF16), true)) return -1; #endif #if defined(CHANNEL_URBDRC_CLIENT) && defined(CHANNEL_RDPECAM_CLIENT) if (!test_rdp_files(argc > 1)) return -1; #endif /* Ascii */ file = freerdp_client_rdp_file_new(); settings = freerdp_settings_new(0); if (!file || !settings) { printf("rdp_file_new failed\n"); goto fail; } if (!freerdp_client_parse_rdp_file_buffer(file, (BYTE*)testRdpFileUTF8, sizeof(testRdpFileUTF8))) goto fail; if (!freerdp_client_populate_settings_from_rdp_file(file, settings)) goto fail; if (freerdp_settings_get_bool(settings, FreeRDP_UseMultimon)) { printf("UseMultiMon mismatch: Actual: %" PRIu32 ", Expected: 0\n", freerdp_settings_get_bool(settings, FreeRDP_UseMultimon)); return -1; } if (!freerdp_settings_get_bool(settings, FreeRDP_Fullscreen)) { printf("ScreenModeId mismatch: Actual: %" PRIu32 ", Expected: TRUE\n", freerdp_settings_get_bool(settings, FreeRDP_Fullscreen)); return -1; } if (strcmp(freerdp_settings_get_string(settings, FreeRDP_ServerHostname), "LAB1-W7-DM-01.lab1.awake.global") != 0) { printf("ServerHostname mismatch: Actual: %s, Expected: %s\n", freerdp_settings_get_string(settings, FreeRDP_ServerHostname), "LAB1-W7-DM-01.lab1.awake.global"); goto fail; } if (strcmp(freerdp_settings_get_string(settings, FreeRDP_GatewayHostname), "LAB1-W2K8R2-GW.lab1.awake.local") != 0) { printf("GatewayHostname mismatch: Actual: %s, Expected: %s\n", freerdp_settings_get_string(settings, FreeRDP_GatewayHostname), "LAB1-W2K8R2-GW.lab1.awake.local"); goto fail; } iValue = freerdp_client_rdp_file_get_integer_option(file, "dynamic resolution"); if (iValue != 1080) { printf("dynamic resolution uses invalid default value %d", iValue); goto fail; } if (!freerdp_settings_get_bool(settings, FreeRDP_DynamicResolutionUpdate)) { printf("FreeRDP_DynamicResolutionUpdate has invalid value"); goto fail; } iValue = freerdp_client_rdp_file_get_integer_option(file, "desktopscalefactor"); if (iValue != 1080) { printf("desktopscalefactor uses invalid default value %d", iValue); goto fail; } if ((INT64)freerdp_settings_get_uint32(settings, FreeRDP_DesktopScaleFactor) != iValue) { printf("FreeRDP_DesktopScaleFactor has invalid value"); goto fail; } /* Check [MS-RDPECAM] related options */ #if defined(CHANNEL_RDPECAM_CLIENT) { ADDIN_ARGV* args = nullptr; iValue = freerdp_client_rdp_file_get_integer_option(file, "encode redirected video capture"); if (iValue != 1) { printf("encode redirected video capture uses invalid default value %d", iValue); goto fail; } iValue = freerdp_client_rdp_file_get_integer_option( file, "redirected video capture encoding quality"); if (iValue != 2) { printf("redirected video capture encoding quality uses invalid default value %d", iValue); goto fail; } args = freerdp_dynamic_channel_collection_find(settings, RDPECAM_DVC_CHANNEL_NAME); if (!args) { printf("rdpecam channel was not loaded"); goto fail; } if (args->argc != 6) { printf("rdpecam channel was not loaded"); goto fail; } for (int x = 0; x < args->argc; x++) { if (strcmp(args->argv[x], camera_args[x]) != 0) { printf("rdpecam invalid argument argv[%d]: %s", x, args->argv[x]); goto fail; } } } #endif /* Check [URBDRC] related options */ #if defined(CHANNEL_URBDRC_CLIENT) { ADDIN_ARGV* args = freerdp_dynamic_channel_collection_find(settings, "urbdrc"); if (!args) { printf("urbdrc channel was not loaded"); goto fail; } if (args->argc != 4) { printf("urbdrc channel was not loaded"); goto fail; } for (int x = 0; x < args->argc; x++) { if (strcmp(args->argv[x], urbdrc_args[x]) != 0) { printf("urbdrc invalid argument argv[%d]: %s", x, args->argv[x]); goto fail; } } } #endif /* Validate selectedmonitors:s:3,2,42,23 */ uValue = freerdp_settings_get_uint32(settings, FreeRDP_NumMonitorIds); if (uValue != 4) { printf("FreeRDP_NumMonitorIds has invalid value %" PRIu32, uValue); goto fail; } puValue = (const UINT32*)freerdp_settings_get_pointer_array(settings, FreeRDP_MonitorIds, 0); if (!puValue) { printf("FreeRDP_MonitorIds has invalid value %p", (const void*)puValue); goto fail; } if ((puValue[0] != 3) || (puValue[1] != 2) || (puValue[2] != 42) || (puValue[3] != 23)) { printf("FreeRDP_MonitorIds has invalid values: [%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 "]", puValue[0], puValue[1], puValue[2], puValue[3]); goto fail; } iValue = freerdp_client_rdp_file_get_integer_option(file, "videoplaybackmode"); if (iValue != 2) { printf("videoplaybackmode uses invalid default value %d", iValue); goto fail; } if (!freerdp_settings_get_bool(settings, FreeRDP_SupportVideoOptimized)) { printf("FreeRDP_SupportVideoOptimized has invalid value"); goto fail; } if (!freerdp_settings_get_bool(settings, FreeRDP_SupportGeometryTracking)) { printf("FreeRDP_SupportGeometryTracking has invalid value"); goto fail; } iValue = freerdp_client_rdp_file_get_integer_option(file, "vendor integer"); if (iValue != 123) goto fail; if (freerdp_client_rdp_file_set_integer_option(file, "vendor integer", 456) == -1) { printf("failed to set integer: vendor integer"); goto fail; } iValue = freerdp_client_rdp_file_get_integer_option(file, "vendor integer"); if (iValue != 456) return -1; const char microsoft[] = "microsoft"; sValue = freerdp_client_rdp_file_get_string_option(file, "vendor string"); if (strncmp(sValue, microsoft, sizeof(microsoft)) != 0) goto fail; const char apple[] = "apple"; freerdp_client_rdp_file_set_string_option(file, "vendor string", "apple"); sValue = freerdp_client_rdp_file_get_string_option(file, "vendor string"); if (strncmp(sValue, apple, sizeof(apple)) != 0) goto fail; freerdp_client_rdp_file_set_string_option(file, "fruits", "banana,oranges"); if (freerdp_client_rdp_file_set_integer_option(file, "numbers", 123456789) == -1) { printf("failed to set integer: numbers"); return -1; } freerdp_client_rdp_file_free(file); tmp = GetKnownPath(KNOWN_PATH_TEMP); if (!tmp) goto fail; base = append("%s/rdp-file-test-%" PRIx64, tmp, id); if (!base) goto fail; if (!CreateDirectoryA(base, nullptr)) goto fail; utfname = append("%s/utfname", base); uniname = append("%s/uniname", base); file = freerdp_client_rdp_file_new(); if (!file || !utfname || !uniname) goto fail; if (!freerdp_client_populate_rdp_file_from_settings(file, settings)) goto fail; if (!freerdp_client_write_rdp_file(file, utfname, FALSE)) goto fail; if (!freerdp_client_write_rdp_file(file, uniname, TRUE)) goto fail; rc = 0; fail: if (utfname) winpr_DeleteFile(utfname); if (uniname) winpr_DeleteFile(uniname); if (base) winpr_RemoveDirectory(base); free(utfname); free(uniname); free(base); free(tmp); freerdp_client_rdp_file_free(file); freerdp_settings_free(settings); return rc; }