log = mfLoghandler::singleton(); $this->baseurl = rtrim($baseurl, '/'); $this->username = $username; $this->password = $password; if (!$this->baseurl || !$this->username || !$this->password) { throw new Exception("Invalid Arguments"); } } /** * Authenticate and retrieve JWT token * @return bool * @throws Exception */ private function _authenticate() { $session_key = "genieacs.{$this->baseurl}.jwt"; $session = new mfConfig($session_key); // Check if we have a valid cached token (valid for 1 hour) if ($session->value() && (time() - $session->edit) < 3600) { $this->jwt_token = $session->value(); return true; } $url = $this->baseurl . '/login'; $ctx_options = [ "http" => [ "ignore_errors" => true, "method" => "POST", "header" => [ "Accept: application/json, text/*", "Content-Type: application/json; charset=UTF-8", ], "content" => json_encode([ "username" => $this->username, "password" => $this->password, ]), ] ]; $ctx = stream_context_create($ctx_options); $response = file_get_contents($url, false, $ctx); // Extract JWT from response headers if (isset($http_response_header)) { foreach ($http_response_header as $header) { if (stripos($header, 'set-cookie') !== false && stripos($header, 'genieacs-ui-jwt=') !== false) { preg_match('/genieacs-ui-jwt=([^;]+)/', $header, $matches); if (isset($matches[1])) { $this->jwt_token = $matches[1]; // Cache the token $session->value($this->jwt_token); $session->save(); return true; } } } } throw new Exception("Authentication failed - could not retrieve JWT token"); } /** * Make a GET request to the API * @param string $endpoint * @return array|null * @throws Exception */ private function _get($endpoint) { if (!$this->jwt_token) { $this->_authenticate(); } $url = $this->baseurl . $endpoint; $ctx_options = [ 'http' => [ 'ignore_errors' => true, 'method' => 'GET', 'header' => [ 'Cookie: genieacs-ui-jwt=' . $this->jwt_token, 'Accept: application/json', ], ] ]; $ctx = stream_context_create($ctx_options); $response = file_get_contents($url, false, $ctx); // Check if we got a 401 and need to re-authenticate if (isset($http_response_header)) { foreach ($http_response_header as $header) { if (stripos($header, 'HTTP/') === 0 && stripos($header, '401') !== false) { // Token expired, re-authenticate and retry $this->jwt_token = null; $this->_authenticate(); return $this->_get($endpoint); } } } return json_decode($response, true); } /** * Make a POST request to the API * @param string $endpoint * @param array $data * @return array|null * @throws Exception */ private function _post($endpoint, $data) { if (!$this->jwt_token) { $this->_authenticate(); } $url = $this->baseurl . $endpoint; $jsonData = json_encode($data); $ctx_options = [ 'http' => [ 'ignore_errors' => true, 'method' => 'POST', 'header' => [ 'Cookie: genieacs-ui-jwt=' . $this->jwt_token, 'Accept: application/json', 'Content-Type: application/json', 'Content-Length: ' . strlen($jsonData) ], 'content' => $jsonData, ] ]; $ctx = stream_context_create($ctx_options); $response = file_get_contents($url, false, $ctx); // Log for debugging error_log("GenieACS POST to $url: " . $jsonData); error_log("GenieACS response: " . ($response ?: 'empty')); // Check if we got a 401 and need to re-authenticate if (isset($http_response_header)) { foreach ($http_response_header as $header) { error_log("GenieACS response header: " . $header); if (stripos($header, 'HTTP/') === 0 && stripos($header, '401') !== false) { // Token expired, re-authenticate and retry $this->jwt_token = null; $this->_authenticate(); return $this->_post($endpoint, $data); } } } // If response is empty or false, it might still be successful (204 No Content) if ($response === false || $response === '') { // Check if status code indicates success if (isset($http_response_header)) { foreach ($http_response_header as $header) { if (stripos($header, 'HTTP/') === 0) { if (stripos($header, '200') !== false || stripos($header, '202') !== false || stripos($header, '204') !== false) { return ['success' => true]; } } } } } return json_decode($response, true); } /** * Get all devices * @return array|null * @throws Exception */ public function getDevices() { return $this->_get('/api/devices'); } /** * Get a specific device by ID * @param string $deviceId Device ID (will be URL-encoded automatically) * @return array|null * @throws Exception */ public function getDevice($deviceId) { return $this->_get('/api/devices/' . rawurlencode($deviceId)); } /** * Create a task for a device * @param string $deviceId Device ID (will be URL-encoded automatically) * @param array $tasks Array of tasks to execute * @return array|null * @throws Exception * * Example: * $tasks = [ * ["name" => "getParameterValues", "parameterNames" => ["InternetGatewayDevice.User.1.Username"]] * ]; */ public function createTask($deviceId, $tasks) { return $this->_post('/api/devices/' . rawurlencode($deviceId) . '/tasks', $tasks); } /** * Get parameter values from a device * @param string $deviceId URL-encoded device ID * @param array $parameterNames Array of parameter names to retrieve * @return array|null * @throws Exception */ public function getParameterValues($deviceId, $parameterNames) { $tasks = [ [ "name" => "getParameterValues", "parameterNames" => $parameterNames ] ]; return $this->createTask($deviceId, $tasks); } /** * Set parameter values on a device * @param string $deviceId URL-encoded device ID * @param array $parameterValues Array of parameter name => value pairs * @return array|null * @throws Exception */ public function setParameterValues($deviceId, $parameterValues) { $tasks = [ [ "name" => "setParameterValues", "parameterValues" => $parameterValues ] ]; return $this->createTask($deviceId, $tasks); } /** * Refresh device information * @param string $deviceId URL-encoded device ID * @return array|null * @throws Exception */ public function refreshDevice($deviceId) { $tasks = [ [ "name" => "refreshObject", "objectName" => "" ] ]; return $this->createTask($deviceId, $tasks); } /** * Reboot a device * @param string $deviceId URL-encoded device ID * @return array|null * @throws Exception */ public function rebootDevice($deviceId) { $tasks = [ [ "name" => "reboot" ] ]; return $this->createTask($deviceId, $tasks); } /** * Factory reset a device * @param string $deviceId URL-encoded device ID * @return array|null * @throws Exception */ public function factoryResetDevice($deviceId) { $tasks = [ [ "name" => "factoryReset" ] ]; return $this->createTask($deviceId, $tasks); } /** * Ping an IP address * @param string $ip IP address to ping * @return array|null * @throws Exception * * Returns: { * "packetsTransmitted": 3, * "packetsReceived": 3, * "packetLoss": 0, * "min": 2.674, * "avg": 3.054, * "max": 3.34, * "mdev": 0.28 * } */ public function ping($ip) { return $this->_get('/api/ping/' . $ip); } /** * Download a file from a device * @param string $deviceId URL-encoded device ID * @param string $fileType File type to download * @param string $fileName Optional file name * @return array|null * @throws Exception */ public function downloadFile($deviceId, $fileType, $fileName = null) { $task = [ "name" => "download", "fileType" => $fileType ]; if ($fileName) { $task["fileName"] = $fileName; } return $this->createTask($deviceId, [$task]); } /** * Parse device data from API response to extract useful information * @param array $deviceData Raw device data from API * @return array Parsed device information */ public static function parseDeviceData($deviceData) { $parsed = []; foreach ($deviceData as $key => $value) { // Extract simple values if (isset($value['value']) && is_array($value['value'])) { $parsed[$key] = $value['value'][0]; } } return $parsed; } /** * Get device ID from device data * @param array $deviceData Raw device data from API * @return string|null Device ID */ public static function getDeviceId($deviceData) { if (isset($deviceData['DeviceID.ID']['value'][0])) { return $deviceData['DeviceID.ID']['value'][0]; } return null; } /** * Get MAC address from device data * @param array $deviceData Raw device data from API * @return string|null MAC address */ public static function getMacAddress($deviceData) { // Try WAN connection MAC address first if (isset($deviceData['InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.MACAddress']['value'][0])) { return $deviceData['InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.MACAddress']['value'][0]; } return null; } /** * Get external IP address from device data * @param array $deviceData Raw device data from API * @return string|null External IP address */ public static function getExternalIP($deviceData) { // Try to get from WAN IP Connection if (isset($deviceData['InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress']['value'][0])) { return $deviceData['InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress']['value'][0]; } // Try alternative connection if (isset($deviceData['InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.2.ExternalIPAddress']['value'][0])) { return $deviceData['InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.2.ExternalIPAddress']['value'][0]; } return null; } /** * Get management/local IP address from device data (private IP) * @param array $deviceData Raw device data from API * @return string|null Management IP address */ public static function getManagementIP($deviceData) { // Check both WAN connections and return the one with a private IP $ips = []; if (isset($deviceData['InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress']['value'][0])) { $ips[] = $deviceData['InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress']['value'][0]; } if (isset($deviceData['InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.2.ExternalIPAddress']['value'][0])) { $ips[] = $deviceData['InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.2.ExternalIPAddress']['value'][0]; } // Return the first private IP found (10.x.x.x, 172.16-31.x.x, 192.168.x.x) foreach ($ips as $ip) { if (self::isPrivateIP($ip)) { return $ip; } } // If no private IP found, return first IP return $ips[0] ?? null; } /** * Check if an IP address is in a private range * @param string $ip IP address * @return bool True if private IP */ private static function isPrivateIP($ip) { $parts = explode('.', $ip); if (count($parts) !== 4) return false; $first = (int)$parts[0]; $second = (int)$parts[1]; // 10.0.0.0 - 10.255.255.255 if ($first === 10) return true; // 172.16.0.0 - 172.31.255.255 if ($first === 172 && $second >= 16 && $second <= 31) return true; // 192.168.0.0 - 192.168.255.255 if ($first === 192 && $second === 168) return true; return false; } /** * Get device manufacturer, model, and version info * @param array $deviceData Raw device data from API * @return array Device info */ public static function getDeviceInfo($deviceData) { return [ 'manufacturer' => $deviceData['DeviceID.Manufacturer']['value'][0] ?? null, 'productClass' => $deviceData['DeviceID.ProductClass']['value'][0] ?? null, 'oui' => $deviceData['DeviceID.OUI']['value'][0] ?? null, 'serialNumber' => $deviceData['DeviceID.SerialNumber']['value'][0] ?? null, 'hardwareVersion' => $deviceData['InternetGatewayDevice.DeviceInfo.HardwareVersion']['value'][0] ?? null, 'softwareVersion' => $deviceData['InternetGatewayDevice.DeviceInfo.SoftwareVersion']['value'][0] ?? null, ]; } }