@@ -179,6 +179,9 @@ private void ApplyRtspTemplate(string vendor)
179179 "xm" => (
180180 $ "rtsp://{ host } :554/user={ user } &password={ Password } &channel=1&stream=0.sdp?real_stream",
181181 $ "rtsp://{ host } :554/user={ user } &password={ Password } &channel=1&stream=1.sdp?real_stream") ,
182+ "xm-sofia" => (
183+ $ "rtsp://{ host } :554/user={ user } &password={ SofiaHash ( Password ) } &channel=1&stream=0.sdp?real_stream",
184+ $ "rtsp://{ host } :554/user={ user } &password={ SofiaHash ( Password ) } &channel=1&stream=1.sdp?real_stream") ,
182185 "hikvision" => (
183186 $ "rtsp://{ host } :554/Streaming/Channels/101",
184187 $ "rtsp://{ host } :554/Streaming/Channels/102") ,
@@ -198,6 +201,23 @@ private void ApplyRtspTemplate(string vendor)
198201 } ;
199202 }
200203
204+ // XM/Xiongmai "Sofia" password digest (NETSurveillance/DVRIP): pairs of MD5
205+ // bytes summed mod 62, mapped to [0-9A-Za-z], 8 chars. Newer XM firmwares
206+ // reject the plaintext password in the RTSP URL and want this instead
207+ // (empty password hashes to the well-known "tlJwpbo6"). MD5 here is a
208+ // protocol requirement, not our choice of cryptography.
209+ private static string SofiaHash ( string password )
210+ {
211+ var md5 = System . Security . Cryptography . MD5 . HashData ( System . Text . Encoding . UTF8 . GetBytes ( password ) ) ;
212+ var chars = new char [ 8 ] ;
213+ for ( var i = 0 ; i < 8 ; i ++ )
214+ {
215+ var n = ( md5 [ 2 * i ] + md5 [ 2 * i + 1 ] ) % 62 ;
216+ chars [ i ] = ( char ) ( n < 10 ? '0' + n : n < 36 ? 'A' + n - 10 : 'a' + n - 36 ) ;
217+ }
218+ return new string ( chars ) ;
219+ }
220+
201221 [ RelayCommand ( CanExecute = nameof ( CanTestConnection ) ) ]
202222 private async Task TestConnectionAsync ( )
203223 {
0 commit comments