diff --git a/classes/AuthenticationResource.class.php b/classes/AuthenticationResource.class.php index a486839..93fec0c 100644 --- a/classes/AuthenticationResource.class.php +++ b/classes/AuthenticationResource.class.php @@ -51,7 +51,7 @@ function __construct($parameters) { break; } } catch (Exception $e) { - trigger_error($e->message, E_USER_ERROR); + trigger_error($e, E_USER_ERROR); } } diff --git a/classes/ZoneFunctions.class.php b/classes/ZoneFunctions.class.php index 1679128..771b338 100644 --- a/classes/ZoneFunctions.class.php +++ b/classes/ZoneFunctions.class.php @@ -66,6 +66,7 @@ public function get_all_zones($response, &$out = null, $query = null) { $output = array(); while (($row = $result->fetch(PDO::FETCH_ASSOC)) !== false ) { $zone = array(); + $zone['z_id'] = $row['z_id']; $zone['name'] = $row['z_name']; $zone['type'] = $row['z_type']; if (!empty($row['z_master'])) { $zone['master'] = $row['z_master']; } @@ -86,7 +87,7 @@ public function query_zones($response, $query, &$out = null) { return ZoneFunctions::get_all_zones($response, $out, $query); } - public function get_zone($response, $identifier, &$out = null, $details = true, $arpa_expand = true, $include_zone_id = false) { + public function get_zone($response, $identifier, &$out = null, $details = true, $arpa_expand = true, $include_zone_id = true) { $arpa = null; if (preg_match(VALID_IPV4, $identifier) === 1) { $arpa = HelperFunctions::ipv4_to_arpa($identifier); @@ -162,6 +163,7 @@ public function get_zone($response, $identifier, &$out = null, $details = true, } $record = array(); + $record['r_id'] = $row['r_id']; $record['name'] = $row['r_name']; $record['type'] = $row['r_type']; $record['content'] = $row['r_content']; @@ -215,7 +217,9 @@ public function create_zone($response, $data, &$out = null) { foreach ($p['entries'] as $entry) { $e = new stdClass(); $e->name = str_replace(array("[ZONE]"), array($data->name), $entry['name']); - $e->content = str_replace(array("[ZONE]"), array($data->name), $entry['content']); + $e->content = str_replace( + array("[ZONE]","[SERIAL]"), + array($data->name,date("Ymd00")), $entry['content']); $e->type = $entry['type']; $e->ttl = $entry['ttl']; $e->priority = $entry['priority']; @@ -494,18 +498,21 @@ public function modify_zone($response, $identifier, $data, &$out = null) { if (isset($data->records)) { $deletions = array(); $additions = array(); + $updates = array(); foreach ($data->records as $record) { if (isset($record->mode) && $record->mode == "delete") { $deletions[] = $record; + } elseif (isset($record->mode) && $record->mode == "update") { + $updates[] = $record; } else { $additions[] = $record; } } - + $records_responses = array(); if (!empty($deletions)) { $obj = new StdClass(); $obj->records = $deletions; - ZoneFunctions::delete_records($response, $identifier, $obj, $out, $connection); + $records_responses[] = ZoneFunctions::delete_records($response, $identifier, $obj, $out, $connection); if (!$out) { $connection->rollback(); @@ -516,10 +523,24 @@ public function modify_zone($response, $identifier, $data, &$out = null) { unset($obj); } + if (!empty($updates)) { + $obj = new StdClass(); + $obj->records = $updates; + $records_responses[] = ZoneFunctions::update_records($response, $identifier, $obj, $out, $connection); + + if (!$out) { + $connection->rollback(); + $out = false; + return $response; + } + + unset($obj); + } + if (!empty($additions)) { $obj = new StdClass(); $obj->records = $additions; - ZoneFunctions::create_records($response, $identifier, $obj, $out, true, $connection); + $records_responses[] = ZoneFunctions::create_records($response, $identifier, $obj, $out, true, $connection); if (!$out) { $connection->rollback(); @@ -536,7 +557,10 @@ public function modify_zone($response, $identifier, $data, &$out = null) { $response->code = Response::OK; $response->body = true; if (isset($data->records)) { - $response->log_message = sprintf("Zone %s was modified, deleted %d records and added %d records.", $identifier, count($deletions), count($additions)); + $response->log_message = sprintf("Zone %s was modified: [%s]", + $identifier, + implode("|",array_map(function ($v){return $v->log_message;}, $records_responses)) + ); } else { $response->log_message = sprintf("Zone %s was modified.", $identifier); } @@ -552,7 +576,7 @@ public function delete_zone($response, $identifier, &$out = null) { $out = false; return $response; } - + $z_id = $o['z_id']; unset($o); try { @@ -567,6 +591,21 @@ public function delete_zone($response, $identifier, &$out = null) { $connection->beginTransaction(); + $statement = $connection->prepare(sprintf( + "DELETE FROM `%s` WHERE domain_id = :z_id;", PowerDNSConfig::DB_RECORD_TABLE + )); + + if ($statement->execute(array(":z_id" => $z_id)) === false) { + $response->code = Response::INTERNALSERVERERROR; + $response->error = "Could not query PowerDNS server."; + $response->error_detail = "INTERNAL_SERVER_ERROR"; + + $connection->rollback(); + $out = false; + + return $response; + } + $response->log_message = sprintf("Zone's %s records were deleted. ", $identifier); $statement = $connection->prepare(sprintf( "DELETE FROM `%s` WHERE name = :name;", PowerDNSConfig::DB_ZONE_TABLE )); @@ -586,12 +625,105 @@ public function delete_zone($response, $identifier, &$out = null) { $response->code = Response::OK; $response->body = true; - $response->log_message = sprintf("Zone %s was deleted.", $identifier); + $response->log_message .= sprintf("Zone %s was deleted.", $identifier); $out = true; return $response; } - + public function update_records($response, $identifier, $data, &$out = null, $connection = null) { + if (!is_object($data) || !isset($data->records)) { + $response->code = Response::INTERNALSERVERERROR; + $response->error = "Updates data invalid"; + $response->error_detail = "INTERNAL_SERVER_ERROR"; + $out = false; + return $response; + } + + ZoneFunctions::get_zone($response, $identifier, $o, false, true, true); + + if (empty($o)) { + $out = false; + return $response; + } + + if ($o['type'] == "SLAVE") { + $response->code = Response::CONFLICT; + $response->error = sprintf("Cannot update records from SLAVE zone %s", $identifier); + $response->error_detail = "ZONE_IS_SLAVE"; + $out = false; + return $response; + } + + $zone_id = $o['z_id']; + + if (empty($zone_id) || !ctype_digit($zone_id)) { + $response->code = Response::INTERNALSERVERERROR; + $response->error = "Could not retrieve Zone ID to delete records from" . var_export($o, true); + $response->error_detail = "INTERNAL_SERVER_ERROR"; + $out = false; + return $response; + } + + unset($o); + + $commit = false; + if ($connection === null) { + try { + $connection = Database::getConnection(); + } catch (PDOException $e) { + $response->code = Response::INTERNALSERVERERROR; + $response->error = "Could not connect to PowerDNS server."; + $response->error_detail = "INTERNAL_SERVER_ERROR"; + $out = false; + return $response; + } + $connection->beginTransaction(); + $commit = true; + } + + $updated = 0; + foreach ($data->records as $record) { + if ( !isset($record->r_id) ) { + continue; + } + //need at least 3 attributes. mode, r_id and something to update + if ( isset( $record->r_id ) && count( (array) $record) < 3 ){ + continue; + } + foreach ( array_keys( (array)$record ) as $f_name ){ + if ( !in_array($f_name, array( 'prio','content','name','ttl','type') ) ) continue; + $stmt = $connection->prepare(sprintf( + "UPDATE `%s` SET `%s` = :f_value WHERE domain_id = %s AND id = :r_id;", PowerDNSConfig::DB_RECORD_TABLE,$f_name,$zone_id + )); + $stmt->bindParam(":r_id", $record->r_id); + $stmt->bindParam(":f_value", $record->$f_name); + //$r_id = $record->r_id; + //$f_value = $record->$f_name; + if ($stmt->execute() === false) { + $response->code = Response::INTERNALSERVERERROR; + $response->error = sprintf("Rolling back transaction, failed to update zone record - did: '%s', r_id: '%s', key: '%s', value: '%s'", $zone_id,$record->r_id, $f_name, $record->$f_name); + $response->error_detail = "RECORD_UPDATE_FAILED"; + if ($commit == true) { + $connection->rollback(); + } + $out = false; + return $response; + } + $updated++; + } + } + + if ($commit == true) { + $connection->commit(); + } + + $response->code = Response::OK; + $response->body = true; + $response->log_message = sprintf("Zone %s updated %d of %d records.", $identifier, $updated,count($data->records)); + $out = true; + + return $response; + } public function delete_records($response, $identifier, $data, &$out = null, $connection = null) { if (!is_object($data) || !isset($data->records)) { $response->code = Response::INTERNALSERVERERROR; @@ -639,10 +771,10 @@ public function delete_records($response, $identifier, $data, &$out = null, $con $out = false; return $response; } - $connection->beginTransaction(); $commit = true; } + $statement = $connection->prepare(sprintf( "DELETE FROM `%s` WHERE domain_id = :did AND name = :name AND type = :type AND prio = :priority AND content = :content;", PowerDNSConfig::DB_RECORD_TABLE @@ -663,37 +795,41 @@ public function delete_records($response, $identifier, $data, &$out = null, $con $statement_noprio->bindParam(":type", $r_type); $statement_noprio->bindParam(":content", $r_content); + $statement_rid = $connection->prepare(sprintf("DELETE FROM `%s` WHERE id = :rid",PowerDNSConfig::DB_RECORD_TABLE)); + $statement_rid->bindParam(":rid", $r_rid); + $r_did = $zone_id; foreach ($data->records as $record) { $stmt = $statement_noprio; - if (!isset($record->name) || !isset($record->type) || !isset($record->content)) { + if ( (!isset($record->name) || !isset($record->type) || !isset($record->content)) && !isset($record->r_id) ) { continue; } - - if (($record->type == "MX") || ($record->type == "SRV")) { - if (!isset($record->priority)) { - continue; - } else { - $stmt = $statement; + if ( isset( $record->r_id ) ){ + $stmt = $statement_rid; + $r_rid = $record->r_id; + } else { + if (($record->type == "MX") || ($record->type == "SRV")) { + if (!isset($record->priority)) { + continue; + } else { + $stmt = $statement; + } } + + $r_name = $record->name; + $r_type = $record->type; + $r_content = $record->content; + $r_prio = $record->priority; } - - $r_name = $record->name; - $r_type = $record->type; - $r_content = $record->content; - $r_prio = $record->priority; - if ($stmt->execute() === false) { $response->code = Response::INTERNALSERVERERROR; $response->error = sprintf("Rolling back transaction, failed to delete zone record - name: '%s', type: '%s', prio: '%s'", $r_name, $r_type, $r_prio); $response->error_detail = "RECORD_DELETE_FAILED"; - if ($commit == true) { $connection->rollback(); } $out = false; - return $response; } } diff --git a/codes.txt b/codes.txt index ebaf187..73634bb 100644 --- a/codes.txt +++ b/codes.txt @@ -21,6 +21,7 @@ RECORD_CNAME_OTHER_INSERT RECORD_CNAME_OTHER_PRESENT RECORD_DELETE_FAILED RECORD_INSERT_FAILED +RECORD_UPDATE_FAILED RECORD_INVALID_CHANGE_DATE RECORD_INVALID_CODE RECORD_INVALID_NAME diff --git a/lib/pdo_token_backend.php b/lib/pdo_token_backend.php index 44d6f5d..ab2eaac 100644 --- a/lib/pdo_token_backend.php +++ b/lib/pdo_token_backend.php @@ -68,7 +68,7 @@ public function __construct() { try { $this->connection = Database::getConnection(); } catch (Exception $e) { - throw new Exception("Failed to open database connection"); + throw new Exception("Failed to open database connection: $e"); } } diff --git a/lib/tonic.php b/lib/tonic.php index 30a64b5..c42a160 100644 --- a/lib/tonic.php +++ b/lib/tonic.php @@ -322,6 +322,11 @@ function __construct($config = array()) { $raw_headers = apache_request_headers(); } else if (function_exists("nsapi_request_headers")) { $raw_headers = nsapi_request_headers(); + } else if (substr(php_sapi_name(), -3) == 'cgi' ) { + $this->requestType = $_SERVER['HTTP_CONTENT_TYPE']; + if (isset( $_SERVER['HTTP_X_AUTHENTICATION_TOKEN'] )){ + $this->requestToken = $_SERVER['HTTP_X_AUTHENTICATION_TOKEN']; + } } foreach ($raw_headers as $k => $h) { switch (strtolower($k)) { @@ -761,7 +766,6 @@ class NoResource extends Resource { * @return Response */ function exec($request) { - // send 404 not found $response = new Response($request); $response->code = Response::NOTFOUND;