Created
September 21, 2024 05:45
-
-
Save troykelly/f4ec21bfc92178389fc4a5c4f94a0f8b to your computer and use it in GitHub Desktop.
Reboots cheap Aliexpress et. al. "box" cameras
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # | |
| # reset_camera.sh | |
| # | |
| # Purpose: | |
| # A Bash script to log in to and reset a web camera. | |
| # To reset the camera, the script must first log in and then set appropriate | |
| # cookies and send the reboot subject and XML payload. | |
| # | |
| # Author: Troy Kelly | |
| # Contact: troy@troykelly.com | |
| # Code History: | |
| # - 2024-09-21: Initial version. | |
| # | |
| # Usage: | |
| # ./reset_camera.sh --host <hostname> --username <username> --password <password> [--timeout <timeout>] | |
| # | |
| set -euo pipefail | |
| # Catch signals and perform cleanup | |
| cleanup() { | |
| echo "An error occurred. Exiting." >&2 | |
| exit 1 | |
| } | |
| trap cleanup SIGINT SIGTERM SIGHUP ERR | |
| # Default values | |
| CAMERA_HOSTNAME="" | |
| CAMERA_USERNAME="" | |
| CAMERA_PASSWORD="" | |
| ALIVE_TIMEOUT=15 | |
| # Camera URLs and payloads | |
| CAMERA_LOGIN="login.html" | |
| CAMERA_DO_LOGIN="/goform/formLogin" | |
| CAMERA_SET="/action/set" | |
| CAMERA_GET="/action/get" | |
| CAMERA_SUBJECT_REBOOT="maintain" | |
| CAMERA_PAYLOAD_REBOOT='<?xml version="1.0" encoding="utf-8"?><request><maintain><type>0</type></maintain></request>' | |
| # Parse command line arguments | |
| while [[ $# -gt 0 ]]; do | |
| case $1 in | |
| --host|-h) | |
| if [[ $# -gt 1 ]]; then | |
| CAMERA_HOSTNAME="$2" | |
| shift 2 | |
| else | |
| echo "Error: --host requires a value" >&2 | |
| exit 1 | |
| fi | |
| ;; | |
| --username|-u) | |
| if [[ $# -gt 1 ]]; then | |
| CAMERA_USERNAME="$2" | |
| shift 2 | |
| else | |
| echo "Error: --username requires a value" >&2 | |
| exit 1 | |
| fi | |
| ;; | |
| --password|-p) | |
| if [[ $# -gt 1 ]]; then | |
| CAMERA_PASSWORD="$2" | |
| shift 2 | |
| else | |
| echo "Error: --password requires a value" >&2 | |
| exit 1 | |
| fi | |
| ;; | |
| --timeout|-t) | |
| if [[ $# -gt 1 ]]; then | |
| ALIVE_TIMEOUT="$2" | |
| shift 2 | |
| else | |
| echo "Error: --timeout requires a value" >&2 | |
| exit 1 | |
| fi | |
| ;; | |
| *) | |
| echo "Unknown argument: $1" >&2 | |
| exit 1 | |
| ;; | |
| esac | |
| done | |
| # Check if the camera hostname is set | |
| if [[ -z "$CAMERA_HOSTNAME" ]]; then | |
| echo "Error: Camera hostname not set" >&2 | |
| exit 1 | |
| fi | |
| # Check if the camera username is set | |
| if [[ -z "$CAMERA_USERNAME" ]]; then | |
| echo "Error: Camera username not set" >&2 | |
| exit 1 | |
| fi | |
| # Check if the camera password is set | |
| if [[ -z "$CAMERA_PASSWORD" ]]; then | |
| echo "Error: Camera password not set" >&2 | |
| exit 1 | |
| fi | |
| # Get the 'tid' value from the login URL | |
| get_tid() { | |
| echo "Retrieving 'tid' value from the camera..." >&2 | |
| local login_url="http://${CAMERA_HOSTNAME}/${CAMERA_LOGIN}" | |
| # Use curl to follow redirects and capture the final URL | |
| local final_url | |
| final_url=$(curl -s -I -L "${login_url}" -o /dev/null -w "%{url_effective}") | |
| if [[ -z "${final_url}" ]]; then | |
| echo "Error: Failed to retrieve the final URL" >&2 | |
| exit 1 | |
| fi | |
| # Extract 't' parameter from the final URL | |
| local tid | |
| tid=$(echo "${final_url}" | sed -n 's/.*[?&]t=\([^&]*\).*/\1/p') | |
| if [[ -z "${tid}" ]]; then | |
| echo "Error: Failed to extract 'tid' from the final URL" >&2 | |
| exit 1 | |
| else | |
| echo "Retrieved 'tid' value: ${tid}" >&2 | |
| fi | |
| # Output the tid value to STDOUT | |
| echo "${tid}" | |
| } | |
| # Login to the camera | |
| login() { | |
| echo "Logging into the camera..." >&2 | |
| local tid="$1" | |
| local do_login_url="http://${CAMERA_HOSTNAME}${CAMERA_DO_LOGIN}" | |
| # Generate access value: lowercase MD5 of 'tid:password' | |
| local access_input="${tid}:${CAMERA_PASSWORD}" | |
| local access | |
| access=$(echo -n "${access_input}" | md5 | awk '{print $NF}' | tr '[:upper:]' '[:lower:]') | |
| # Prepare POST data | |
| local login_payload="username=${CAMERA_USERNAME}&access=${access}&tid=${tid}" | |
| # Perform POST request to Do Login URL | |
| # Capture cookies from response | |
| local cookie_file | |
| cookie_file=$(mktemp) | |
| local response_code | |
| response_code=$(curl -s -o /dev/null -w "%{http_code}" -c "${cookie_file}" --data "${login_payload}" "${do_login_url}") | |
| if [[ "${response_code}" != "200" ]]; then | |
| echo "Error: Login failed with response code ${response_code}" >&2 | |
| rm -f "${cookie_file}" | |
| exit 1 | |
| fi | |
| # Extract 'wsid' cookie from cookie file | |
| local wsid | |
| wsid=$(grep 'wsid' "${cookie_file}" | awk '{print $7}') | |
| if [[ -z "${wsid}" ]]; then | |
| echo "Error: Failed to extract 'wsid' from cookies" >&2 | |
| rm -f "${cookie_file}" | |
| exit 1 | |
| else | |
| echo "Login successful. wsid: ${wsid}" >&2 | |
| fi | |
| # Clean up cookie file | |
| rm -f "${cookie_file}" | |
| # Output the wsid value to STDOUT | |
| echo "${wsid}" | |
| } | |
| # Reboot the camera | |
| reboot_camera() { | |
| echo "Initiating camera reboot..." >&2 | |
| local wsid="$1" | |
| # Generate bvpassword: lowercase MD5 of password | |
| local bvpassword | |
| bvpassword=$(echo -n "${CAMERA_PASSWORD}" | md5 | awk '{print $NF}' | tr '[:upper:]' '[:lower:]') | |
| # Prepare cookies | |
| local cookies | |
| cookies="bvusername=${CAMERA_USERNAME}; bvpassword=${bvpassword}; bvlanguage=English; wsid=${wsid}" | |
| # Prepare referer | |
| local nonce_value | |
| nonce_value=$(( RANDOM % 653700 + 800 )) | |
| local referer="http://${CAMERA_HOSTNAME}/nonce.html?slce=${nonce_value}" | |
| # Prepare the URL | |
| local reboot_url="http://${CAMERA_HOSTNAME}${CAMERA_SET}?subject=${CAMERA_SUBJECT_REBOOT}" | |
| # Perform POST request with XML payload and subject querystring | |
| local response_code | |
| response_code=$(curl -s -o /dev/null -w "%{http_code}" \ | |
| --cookie "${cookies}" \ | |
| --header "Referer: ${referer}" \ | |
| --header "Content-Type: application/xml" \ | |
| --data "${CAMERA_PAYLOAD_REBOOT}" \ | |
| "${reboot_url}") | |
| if [[ "${response_code}" != "200" ]]; then | |
| echo "Error: Reboot failed with response code ${response_code}" >&2 | |
| exit 1 | |
| fi | |
| echo "Camera rebooted successfully." >&2 | |
| } | |
| # Main function | |
| main() { | |
| # Get the 'tid' value from the login URL | |
| local tid | |
| tid="$(get_tid)" | |
| # Login to the camera and get wsid | |
| local wsid | |
| wsid="$(login "${tid}")" | |
| # Reboot the camera | |
| reboot_camera "${wsid}" | |
| } | |
| # Run main function | |
| main |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment