From e4a8017b6b26f0d7deaa9664252c458bdff6be25 Mon Sep 17 00:00:00 2001 From: "parvit (Vittorio Parrella)" Date: Fri, 22 Jul 2022 07:08:22 -0400 Subject: [PATCH 1/7] added configuring of authentication for rstp cameras --- src/zm_monitor.cpp | 6 ++++-- src/zm_remote_camera_rtsp.cpp | 8 ++++++-- src/zm_remote_camera_rtsp.h | 5 +++++ src/zm_rtsp.cpp | 19 ++++++++++++------- src/zm_rtsp.h | 4 +++- web/skins/classic/views/monitor.php | 2 ++ 6 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 1e06fe38a..9f19c0b17 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -171,8 +171,8 @@ Monitor::Monitor() //options //host //port - //user - //pass + user(), + pass(), //path //device palette(0), @@ -620,6 +620,8 @@ void Monitor::LoadCamera() { host, // Host port, // Port path, // Path + user, + pass, camera_width, camera_height, rtsp_describe, diff --git a/src/zm_remote_camera_rtsp.cpp b/src/zm_remote_camera_rtsp.cpp index b8a533fae..17e5f6df8 100644 --- a/src/zm_remote_camera_rtsp.cpp +++ b/src/zm_remote_camera_rtsp.cpp @@ -30,6 +30,8 @@ RemoteCameraRtsp::RemoteCameraRtsp( const std::string &p_host, const std::string &p_port, const std::string &p_path, + const std::string &p_user, + const std::string &p_pass, int p_width, int p_height, bool p_rtsp_describe, @@ -47,7 +49,9 @@ RemoteCameraRtsp::RemoteCameraRtsp( p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio), rtsp_describe(p_rtsp_describe), - frameCount(0) + frameCount(0), + user(p_user), + pass(p_pass) { if ( p_method == "rtpUni" ) method = RtspThread::RTP_UNICAST; @@ -110,7 +114,7 @@ void RemoteCameraRtsp::Terminate() { } int RemoteCameraRtsp::Connect() { - rtspThread = zm::make_unique(monitor->Id(), method, protocol, host, port, path, auth, rtsp_describe); + rtspThread = zm::make_unique(monitor->Id(), method, protocol, host, port, path, user, pass, rtsp_describe); return 0; } diff --git a/src/zm_remote_camera_rtsp.h b/src/zm_remote_camera_rtsp.h index 4aadfadf6..b70bf6103 100644 --- a/src/zm_remote_camera_rtsp.h +++ b/src/zm_remote_camera_rtsp.h @@ -38,6 +38,9 @@ protected: int rtcp_sd; bool rtsp_describe; + const std::string user; + const std::string pass; + Buffer buffer; Buffer lastSps; Buffer lastPps; @@ -57,6 +60,8 @@ public: const std::string &host, const std::string &port, const std::string &path, + const std::string &user, + const std::string &pass, int p_width, int p_height, bool p_rtsp_describe, diff --git a/src/zm_rtsp.cpp b/src/zm_rtsp.cpp index e1f3adcd4..edf7b7e9e 100644 --- a/src/zm_rtsp.cpp +++ b/src/zm_rtsp.cpp @@ -135,7 +135,8 @@ RtspThread::RtspThread( const std::string &host, const std::string &port, const std::string &path, - const std::string &auth, + const std::string &user, + const std::string &pass, bool rtsp_describe) : mId(id), mMethod(method), @@ -169,12 +170,16 @@ RtspThread::RtspThread( mHttpSession = stringtf("%d", rand()); mNeedAuth = false; - StringVector parts = Split(auth, ":"); - Debug(2, "# of auth parts %zu", parts.size()); - if ( parts.size() > 1 ) - mAuthenticator = new zm::Authenticator(parts[0], parts[1]); - else - mAuthenticator = new zm::Authenticator(parts[0], ""); + if ( user.length() > 0 && pass.length() > 0 ) { + Debug(2, "# of auth parts 2"); + mAuthenticator = new zm::Authenticator(user, pass); + } else if( user.length() > 0 ) { + Debug(2, "# of auth parts 1"); + mAuthenticator = new zm::Authenticator(user, ""); + } else { + Debug(2, "# of auth parts 0"); + mAuthenticator = new zm::Authenticator("", ""); + } mThread = std::thread(&RtspThread::Run, this); } diff --git a/src/zm_rtsp.h b/src/zm_rtsp.h index 96906e209..8bff6f7c5 100644 --- a/src/zm_rtsp.h +++ b/src/zm_rtsp.h @@ -96,7 +96,9 @@ private: void Run(); public: - RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth, bool rtsp_describe ); + RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, + const std::string &port, const std::string &path, const std::string &user, const std::string &pass, + bool rtsp_describe ); ~RtspThread(); public: diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 675fdb3c9..441ff765d 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -748,6 +748,8 @@ include('_monitor_source_nvsocket.php'); + + From 603d10a072a27990af2dba831aea38f30cf7434c Mon Sep 17 00:00:00 2001 From: "parvit (Vittorio Parrella)" Date: Mon, 15 Aug 2022 12:55:04 -0400 Subject: [PATCH 2/7] janus password integration --- src/zm_monitor_janus.cpp | 44 ++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/zm_monitor_janus.cpp b/src/zm_monitor_janus.cpp index d4c4d007f..99665aa9b 100644 --- a/src/zm_monitor_janus.cpp +++ b/src/zm_monitor_janus.cpp @@ -18,7 +18,9 @@ // #include "zm_monitor.h" +#include +std::string escape_json_string( std::string input ); Monitor::JanusManager::JanusManager(Monitor *parent_) : parent(parent_), @@ -35,29 +37,19 @@ Monitor::JanusManager::JanusManager(Monitor *parent_) : } else { janus_endpoint = "127.0.0.1:8088/janus"; } + + rtsp_username = ""; + rtsp_password = ""; + if( parent->user.length() > 0 ) { + rtsp_username = escape_json_string(parent->user); + rtsp_password = escape_json_string(parent->pass); + } + if (Use_RTSP_Restream) { int restream_port = config.min_rtsp_port; - rtsp_username = ""; - rtsp_password = ""; rtsp_path = "rtsp://127.0.0.1:" + std::to_string(restream_port) + "/" + parent->rtsp_streamname; } else { - std::size_t at_pos = parent->path.find("@", 7); - if (at_pos != std::string::npos) { - //If we find an @ symbol, we have a username/password. Otherwise, passwordless login. - std::size_t colon_pos = parent->path.find(":", 7); //Search for the colon, but only after the rtsp:// text. - if (colon_pos == std::string::npos) { - //Looks like an invalid url - throw std::runtime_error("Cannot Parse URL for Janus."); - } - rtsp_username = parent->path.substr(7, colon_pos-7); - rtsp_password = parent->path.substr(colon_pos+1, at_pos - colon_pos - 1); - rtsp_path = "rtsp://"; - rtsp_path += parent->path.substr(at_pos + 1); - } else { - rtsp_username = ""; - rtsp_password = ""; - rtsp_path = parent->path; - } + rtsp_path = parent->path; } } @@ -164,7 +156,7 @@ int Monitor::JanusManager::add_to_janus() { postData += "\", \"videofmtp\" : \""; postData += profile_override; } - if (rtsp_username != "") { + if (rtsp_username.length() > 0) { postData += "\", \"rtsp_user\" : \""; postData += rtsp_username; postData += "\", \"rtsp_pwd\" : \""; @@ -292,3 +284,15 @@ int Monitor::JanusManager::get_janus_handle() { janus_handle = response.substr(pos + 6, 16); return 1; } //get_janus_handle + +std::string escape_json_string( std::string input ) { + std::string tmp; + tmp = std::regex_replace(input, std::regex("\n"), "\\n"); + tmp = std::regex_replace(tmp, std::regex("\b"), "\\b"); + tmp = std::regex_replace(tmp, std::regex("\f"), "\\f"); + tmp = std::regex_replace(tmp, std::regex("\r"), "\\r"); + tmp = std::regex_replace(tmp, std::regex("\t"), "\\t"); + tmp = std::regex_replace(tmp, std::regex("\""), "\\\""); + tmp = std::regex_replace(tmp, std::regex("\\"), "\\\\"); + return tmp; +} From 408b42edd4f3b9ba4190c33111f5aad3b8c8e810 Mon Sep 17 00:00:00 2001 From: "parvit (Vittorio Parrella)" Date: Mon, 15 Aug 2022 14:30:48 -0400 Subject: [PATCH 3/7] correct regex escaping --- src/zm_monitor_janus.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/zm_monitor_janus.cpp b/src/zm_monitor_janus.cpp index 99665aa9b..e72468eb6 100644 --- a/src/zm_monitor_janus.cpp +++ b/src/zm_monitor_janus.cpp @@ -287,12 +287,12 @@ int Monitor::JanusManager::get_janus_handle() { std::string escape_json_string( std::string input ) { std::string tmp; - tmp = std::regex_replace(input, std::regex("\n"), "\\n"); - tmp = std::regex_replace(tmp, std::regex("\b"), "\\b"); - tmp = std::regex_replace(tmp, std::regex("\f"), "\\f"); - tmp = std::regex_replace(tmp, std::regex("\r"), "\\r"); - tmp = std::regex_replace(tmp, std::regex("\t"), "\\t"); - tmp = std::regex_replace(tmp, std::regex("\""), "\\\""); - tmp = std::regex_replace(tmp, std::regex("\\"), "\\\\"); + tmp = regex_replace(input, regex("\n"), "\\n"); + tmp = regex_replace(tmp, regex("\b"), "\\b"); + tmp = regex_replace(tmp, regex("\f"), "\\f"); + tmp = regex_replace(tmp, regex("\r"), "\\r"); + tmp = regex_replace(tmp, regex("\t"), "\\t"); + tmp = regex_replace(tmp, regex("\""), "\\\""); + tmp = regex_replace(tmp, regex("[\\\\]"), "\\\\"); return tmp; } From 3642abd1551d6e977f4e2dc075fd8ccfc2255660 Mon Sep 17 00:00:00 2001 From: "parvit (Vittorio Parrella)" Date: Mon, 15 Aug 2022 14:35:16 -0400 Subject: [PATCH 4/7] typo --- src/zm_monitor_janus.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/zm_monitor_janus.cpp b/src/zm_monitor_janus.cpp index e72468eb6..630455259 100644 --- a/src/zm_monitor_janus.cpp +++ b/src/zm_monitor_janus.cpp @@ -287,12 +287,12 @@ int Monitor::JanusManager::get_janus_handle() { std::string escape_json_string( std::string input ) { std::string tmp; - tmp = regex_replace(input, regex("\n"), "\\n"); - tmp = regex_replace(tmp, regex("\b"), "\\b"); - tmp = regex_replace(tmp, regex("\f"), "\\f"); - tmp = regex_replace(tmp, regex("\r"), "\\r"); - tmp = regex_replace(tmp, regex("\t"), "\\t"); - tmp = regex_replace(tmp, regex("\""), "\\\""); - tmp = regex_replace(tmp, regex("[\\\\]"), "\\\\"); + tmp = regex_replace(input, std::regex("\n"), "\\n"); + tmp = regex_replace(tmp, std::regex("\b"), "\\b"); + tmp = regex_replace(tmp, std::regex("\f"), "\\f"); + tmp = regex_replace(tmp, std::regex("\r"), "\\r"); + tmp = regex_replace(tmp, std::regex("\t"), "\\t"); + tmp = regex_replace(tmp, std::regex("\""), "\\\""); + tmp = regex_replace(tmp, std::regex("[\\\\]"), "\\\\"); return tmp; } From 442d7b1c89c6ca5b30993a721c3f4ffcbf8e9dbe Mon Sep 17 00:00:00 2001 From: "parvit (Vittorio Parrella)" Date: Thu, 18 Aug 2022 05:05:18 -0400 Subject: [PATCH 5/7] integrated dynamic client and server update of path, user and password values --- web/includes/Monitor.php | 72 +++++++++++++++++++++++++++ web/includes/functions.php | 19 +++++++ web/skins/classic/views/js/monitor.js | 70 ++++++++++++++++++++++++++ web/skins/classic/views/monitor.php | 2 + 4 files changed, 163 insertions(+) diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index f703d4d34..6d631ca16 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -289,6 +289,78 @@ public static function getStatuses() { return $this->{'Server'}; } + public function Path($new=null) { + // set the new value if requested + if( $new !== null ) { + $this->{'Path'} = $new; + } + + $old_us = $this->{'User'}; + $old_ps = $this->{'Pass'}; + + // empty value or old auth values terminate + if( strlen($this->{'Path'}) == 0 ) + return $this->{'Path'}; + + // extract the authentication part from the path given + $values = extract_auth_values_from_url($this->{'Path'}); + + // If no values for User and Pass fields are present then terminate + if( count( $values ) !== 2 ) { + return $this->{'Path'}; + } + + $us = $values[0]; + $ps = $values[1]; + + // Update the auth fields if they were empty and remove them from the path + // or if they are equal between the path and field + if( (strlen($old_us) == 0 || strlen($old_ps) == 0) || + ($us == $old_us && $ps == $old_ps) ) + { + $this->{'Path'} = str_replace("$us:$ps@", "", $this->{'Path'}); + $this->{'User'} = $us; + $this->{'Pass'} = $ps; + } + return $this->{'Path'}; + } + + public function User($new=null) { + if( $new !== null ) { + // no url check if the update has different value + $this->{'User'} = $new; + } + + if( strlen($this->{'User'}) > 0 ) + return $this->{'User'}; + + // Only try to update from path if the field is empty + $values = extract_auth_values_from_url($this->{'Path'}); + if( count( $values ) == 2 ) { + $us = $values[0]; + $this->{'User'} = $values[0]; + } + return $this->{'User'}; + } + + public function Pass($new=null) { + if( $new !== null ) { + // no url check if the update has different value + $this->{'Pass'} = $new; + } + + if( strlen($this->{'Pass'}) > 0 ) + return $this->{'Pass'}; + + // Only try to update from path if the field is empty + $values = extract_auth_values_from_url($this->{'Path'}); + if( count( $values ) == 2 ) { + $ps = $values[1]; + $this->{'Pass'} = $values[1]; + } + return $this->{'Pass'}; + } + public function __call($fn, array $args) { if (count($args)) { if (is_array($this->defaults[$fn]) and $this->defaults[$fn]['type'] == 'set') { diff --git a/web/includes/functions.php b/web/includes/functions.php index 165c8f2c3..11be49921 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -2346,4 +2346,23 @@ function get_subnets($interface) { return $subnets; } +function extract_auth_values_from_url($url): array { + $protocolPrefixPos = strpos($url, '://'); + if( $protocolPrefixPos === false ) + return array(); + + $authSeparatorPos = strpos($url, '@', $protocolPrefixPos+3); + if( $authSeparatorPos === false ) + return array(); + + $fieldsSeparatorPos = strpos($url, ':', $protocolPrefixPos+3); + if( $fieldsSeparatorPos === false || $authSeparatorPos < $fieldsSeparatorPos ) + return array(); + + $username = substr( $url, $protocolPrefixPos+3, $fieldsSeparatorPos-($protocolPrefixPos+3) ); + $password = substr( $url, $fieldsSeparatorPos+1, $authSeparatorPos-$fieldsSeparatorPos-1 ); + + return array( $username, $password ); +} + ?> diff --git a/web/skins/classic/views/js/monitor.js b/web/skins/classic/views/js/monitor.js index a80ea031f..a01669c81 100644 --- a/web/skins/classic/views/js/monitor.js +++ b/web/skins/classic/views/js/monitor.js @@ -302,6 +302,18 @@ function initPage() { } }); + let monitorPath = document.getElementsByName("newMonitor[Path]")[0]; + monitorPath.addEventListener('keyup', change_Path); // on edit sync path -> user & pass + monitorPath.addEventListener('blur', change_Path); // remove fields from path if user & pass equal on end of edit + + let monitorUser = document.getElementsByName("newMonitor[User]"); + if( monitorUser.length > 0 ) + monitorUser[0].addEventListener('blur', change_Path); // remove fields from path if user & pass equal + + let monitorPass = document.getElementsByName("newMonitor[Pass]"); + if( monitorPass.length > 0 ) + monitorPass[0].addEventListener('blur', change_Path); // remove fields from path if user & pass equal + if ( parseInt(ZM_OPT_USE_GEOLOCATION) ) { if ( window.L ) { const form = document.getElementById('contentForm'); @@ -343,6 +355,64 @@ function initPage() { updateLinkedMonitorsUI(); } // end function initPage() +function change_Path(event) { + var pathInput = document.getElementsByName("newMonitor[Path]")[0]; + + var protoPrefixPos = pathInput.value.indexOf('://'); + if( protoPrefixPos == -1 ) + return; + + // check the formatting of the url + var authSeparatorPos = pathInput.value.indexOf( '@', protoPrefixPos+3 ); + if( authSeparatorPos == -1 ) { + console.warn('ignoring URL incorrectly formatted, missing "@"'); + return; + } + + var fieldsSeparatorPos = pathInput.value.indexOf( ':', protoPrefixPos+3 ); + if( authSeparatorPos == -1 || fieldsSeparatorPos >= authSeparatorPos ) { + console.warn('ignoring URL incorrectly formatted, missing ":"'); + return; + } + + var usernameValue = pathInput.value.substring( protoPrefixPos+3, fieldsSeparatorPos ); + var passwordValue = pathInput.value.substring( fieldsSeparatorPos+1, authSeparatorPos ); + if( usernameValue.length == 0 || passwordValue.length == 0 ) { + console.warn('ignoring URL incorrectly formatted, empty username or password'); + return; + } + + // get the username / password inputs + var userInput = document.getElementsByName("newMonitor[User]"); + var passInput = document.getElementsByName("newMonitor[Pass]"); + + if (userInput.length != 1 || passInput.length != 1) { + return + } + + // on editing update the fields only if they are empty or a prefix of the new value + if( event.type != 'blur' ) { + if( userInput[0].value.length == 0 || usernameValue.indexOf(userInput[0].value) == 0 || + userInput[0].value.indexOf(usernameValue) == 0 ) + { + userInput[0].value = usernameValue; + } + + if( passInput[0].value.length == 0 || passwordValue.indexOf(passInput[0].value) == 0 || + passInput[0].value.indexOf(passwordValue) == 0 ) + { + passInput[0].value = passwordValue; + } + + return; + } + + // on leaving the input sync the values and remove it from the url + // only if they already match (to not overwrite already present values) + if( userInput[0].value == usernameValue && passInput[0].value == passwordValue ) + pathInput.value = pathInput.value.substring(0, protoPrefixPos+3) + pathInput.value.substring(authSeparatorPos+1, pathInput.value.length); +} + function change_WebColour() { $j('#WebSwatch').css( 'backgroundColor', diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index a5a9dc2c1..c1b6260de 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -688,6 +688,8 @@ include('_monitor_source_nvsocket.php'); Type() == 'Remote' ) { ?> + + Protocol(), "updateMethods( this );if(this.value=='rtsp'){\$('RTSPDescribe').setStyle('display','table-row');}else{\$('RTSPDescribe').hide();}" ); ?> From a94d74143ef0a7933843bab59eb6861f12638c3c Mon Sep 17 00:00:00 2001 From: "parvit (Vittorio Parrella)" Date: Sat, 20 Aug 2022 10:01:14 -0400 Subject: [PATCH 6/7] support for ffmpeg type camera authentication with special characters --- src/zm_ffmpeg_camera.cpp | 21 +++++++++++++++++++-- src/zm_ffmpeg_camera.h | 8 ++++++-- src/zm_monitor.cpp | 2 ++ src/zm_remote_camera_rtsp.cpp | 4 ++-- src/zm_utils.cpp | 20 ++++++++++++++++++++ src/zm_utils.h | 5 +++++ 6 files changed, 54 insertions(+), 6 deletions(-) diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index b8e284951..577df8dd4 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -85,6 +85,8 @@ FfmpegCamera::FfmpegCamera( const Monitor *monitor, const std::string &p_path, const std::string &p_second_path, + const std::string &p_user, + const std::string &p_pass, const std::string &p_method, const std::string &p_options, int p_width, @@ -114,10 +116,13 @@ FfmpegCamera::FfmpegCamera( ), mPath(p_path), mSecondPath(p_second_path), + mUser(UriEncode(p_user)), + mPass(UriEncode(p_pass)), mMethod(p_method), mOptions(p_options), hwaccel_name(p_hwaccel_name), - hwaccel_device(p_hwaccel_device) + hwaccel_device(p_hwaccel_device), + frameCount(0) { mMaskedPath = remove_authentication(mPath); mMaskedSecondPath = remove_authentication(mSecondPath); @@ -125,7 +130,6 @@ FfmpegCamera::FfmpegCamera( FFMPEGInit(); } - frameCount = 0; mCanCapture = false; error_count = 0; use_hwaccel = true; @@ -276,6 +280,13 @@ int FfmpegCamera::OpenFfmpeg() { std::string protocol = mPath.substr(0, 4); protocol = StringToUpper(protocol); if ( protocol == "RTSP" ) { + if( mUser.length() > 0 ) { + ret = av_dict_set(&opts, "auth_type", "basic", 0); + if (ret < 0) { + Warning("Could not set auth_type method to 'basic'"); + } + } + const std::string method = Method(); if ( method == "rtpMulti" ) { ret = av_dict_set(&opts, "rtsp_transport", "udp_multicast", 0); @@ -308,6 +319,12 @@ int FfmpegCamera::OpenFfmpeg() { mFormatContext->interrupt_callback.opaque = this; mFormatContext->flags |= AVFMT_FLAG_NOBUFFER | AVFMT_FLAG_FLUSH_PACKETS; + if( mUser.length() > 0 ) { + // build the actual uri string with encoded parameters (from the user and pass fields) + mPath = StringToLower(protocol) + "://" + mUser + ":" + mPass + "@" + mMaskedPath.substr(7, std::string::npos); + Debug(1, "Rebuilt URI with encoded parameters: '%s'", mPath.c_str()); + } + ret = avformat_open_input(&mFormatContext, mPath.c_str(), input_format, &opts); if (ret != 0) { logPrintf(Logger::ERROR + monitor->Importance(), diff --git a/src/zm_ffmpeg_camera.h b/src/zm_ffmpeg_camera.h index 289695b36..42ce5ab91 100644 --- a/src/zm_ffmpeg_camera.h +++ b/src/zm_ffmpeg_camera.h @@ -38,6 +38,8 @@ class FfmpegCamera : public Camera { std::string mPath; std::string mMaskedPath; std::string mSecondPath; + std::string mUser; + std::string mPass; std::string mMaskedSecondPath; std::string mMethod; std::string mOptions; @@ -71,8 +73,10 @@ class FfmpegCamera : public Camera { public: FfmpegCamera( const Monitor *monitor, - const std::string &path, - const std::string &second_path, + const std::string &p_path, + const std::string &p_second_path, + const std::string &p_user, + const std::string &p_pass, const std::string &p_method, const std::string &p_options, int p_width, diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 815f1b3a8..b1f45b7a3 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -660,6 +660,8 @@ void Monitor::LoadCamera() { camera = zm::make_unique(this, path, second_path, + user, + pass, method, options, camera_width, diff --git a/src/zm_remote_camera_rtsp.cpp b/src/zm_remote_camera_rtsp.cpp index efe333d67..500801392 100644 --- a/src/zm_remote_camera_rtsp.cpp +++ b/src/zm_remote_camera_rtsp.cpp @@ -49,9 +49,9 @@ RemoteCameraRtsp::RemoteCameraRtsp( p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio), rtsp_describe(p_rtsp_describe), - frameCount(0), user(p_user), - pass(p_pass) + pass(p_pass), + frameCount(0) { if ( p_method == "rtpUni" ) method = RtspThread::RTP_UNICAST; diff --git a/src/zm_utils.cpp b/src/zm_utils.cpp index d60176b2d..d7fbe4a92 100644 --- a/src/zm_utils.cpp +++ b/src/zm_utils.cpp @@ -379,6 +379,26 @@ std::string UriDecode(const std::string &encoded) { return retbuf; } +std::string UriEncode(const std::string &value) { + const char *src = value.c_str(); + std::string retbuf; + retbuf.reserve(value.length() * 3); // at most all characters get replaced with the escape + + char tmp[5] = ""; + while(*src) { + if ( *src == ' ' ) { + retbuf.append("%%20"); + } else if ( !( (*src >= 'a' && *src <= 'z') || (*src >= 'A' && *src <= 'Z') ) ) { + sprintf(tmp, "%%%02X", *src); + retbuf.append(tmp); + } else { + retbuf.push_back(*src); + } + src++; + } + return retbuf; +} + QueryString::QueryString(std::istream &input) { while (!input.eof() && input.peek() > 0) { //Should eat "param1=" diff --git a/src/zm_utils.h b/src/zm_utils.h index 16e4cbb05..bc964092f 100644 --- a/src/zm_utils.h +++ b/src/zm_utils.h @@ -50,6 +50,10 @@ inline std::string StringToUpper(std::string str) { std::transform(str.begin(), str.end(), str.begin(), ::toupper); return str; } +inline std::string StringToLower(std::string str) { + std::transform(str.begin(), str.end(), str.begin(), ::tolower); + return str; +} StringVector Split(const std::string &str, char delim); StringVector Split(const std::string &str, const std::string &delim, size_t limit = 0); @@ -130,6 +134,7 @@ constexpr std::size_t size(const T(&)[N]) noexcept { return N; } std::string mask_authentication(const std::string &url); std::string remove_authentication(const std::string &url); +std::string UriEncode(const std::string &value); std::string UriDecode(const std::string &encoded); class QueryParameter { From ef104fcda3a76e104f0a214b7dd6f679016026b6 Mon Sep 17 00:00:00 2001 From: "parvit (Vittorio Parrella)" Date: Sat, 20 Aug 2022 11:00:51 -0400 Subject: [PATCH 7/7] libvlc support for rtsp authentication with special characters --- src/zm_libvlc_camera.cpp | 17 +++++++++++++++++ src/zm_libvlc_camera.h | 4 +++- src/zm_monitor.cpp | 2 ++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/zm_libvlc_camera.cpp b/src/zm_libvlc_camera.cpp index c1278df12..e875ec20d 100644 --- a/src/zm_libvlc_camera.cpp +++ b/src/zm_libvlc_camera.cpp @@ -104,6 +104,8 @@ void LibvlcUnlockBuffer(void* opaque, void* picture, void *const *planes) { LibvlcCamera::LibvlcCamera( const Monitor *monitor, const std::string &p_path, + const std::string &p_user, + const std::string &p_pass, const std::string &p_method, const std::string &p_options, int p_width, @@ -131,6 +133,8 @@ LibvlcCamera::LibvlcCamera( p_record_audio ), mPath(p_path), + mUser(UriEncode(p_user)), + mPass(UriEncode(p_pass)), mMethod(p_method), mOptions(p_options) { @@ -214,6 +218,8 @@ int LibvlcCamera::PrimeCapture() { opVect = Split(Options(), ","); + Debug(1, "Method: '%s'", Method().c_str()); + // Set transport method as specified by method field, rtpUni is default if ( Method() == "rtpMulti" ) opVect.push_back("--rtsp-mcast"); @@ -241,6 +247,17 @@ int LibvlcCamera::PrimeCapture() { } (*libvlc_log_set_f)(mLibvlcInstance, LibvlcCamera::log_callback, nullptr); + // recreate the path with encoded authentication info + if( mUser.length() > 0 ) { + std::string mMaskedPath = remove_authentication(mPath); + + std::string protocol = StringToUpper(mPath.substr(0, 4)); + if ( protocol == "RTSP" ) { + // build the actual uri string with encoded parameters (from the user and pass fields) + mPath = StringToLower(protocol) + "://" + mUser + ":" + mPass + "@" + mMaskedPath.substr(7, std::string::npos); + Debug(1, "Rebuilt URI with encoded parameters: '%s'", mPath.c_str()); + } + } mLibvlcMedia = (*libvlc_media_new_location_f)(mLibvlcInstance, mPath.c_str()); if ( mLibvlcMedia == nullptr ) { diff --git a/src/zm_libvlc_camera.h b/src/zm_libvlc_camera.h index dd37d8615..62af6f632 100644 --- a/src/zm_libvlc_camera.h +++ b/src/zm_libvlc_camera.h @@ -49,6 +49,8 @@ class LibvlcCamera : public Camera { static void log_callback( void *ptr, int level, const libvlc_log_t *ctx, const char *format, va_list vargs ); protected: std::string mPath; + std::string mUser; + std::string mPass; std::string mMethod; std::string mOptions; StringVector opVect; // mOptArgV will point into opVect so it needs to hang around @@ -62,7 +64,7 @@ protected: libvlc_media_player_t *mLibvlcMediaPlayer; public: - LibvlcCamera( const Monitor *monitor, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ); + LibvlcCamera( const Monitor *monitor, const std::string &path, const std::string &user,const std::string &pass, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ); ~LibvlcCamera(); const std::string &Path() const { return mPath; } diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index b1f45b7a3..59b703386 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -699,6 +699,8 @@ void Monitor::LoadCamera() { #if HAVE_LIBVLC camera = zm::make_unique(this, path.c_str(), + user, + pass, method, options, camera_width,