17 * |
17 * |
18 * You should have received a copy of the GNU General Public License |
18 * You should have received a copy of the GNU General Public License |
19 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
20 */ |
20 */ |
21 |
21 |
|
22 function count_zone_records($zone_id) { |
|
23 global $db; |
|
24 $sqlq = "SELECT COUNT(id) FROM records WHERE domain_id = ".$db->quote($zone_id); |
|
25 $record_count = $db->queryOne($sqlq); |
|
26 return $record_count; |
|
27 } |
|
28 |
22 function update_soa_serial($domain_id) |
29 function update_soa_serial($domain_id) |
23 { |
30 { |
24 global $db; |
31 global $db; |
25 /* |
|
26 * THIS CODE ISNT TESTED THROUGH MUCH YET! |
|
27 * !!!!!!! BETACODE !!!!!!!!!! |
|
28 * Code committed by DeViCeD, Thanks a lot! |
|
29 * Heavily hax0red by Trancer/azurazu |
|
30 * |
|
31 * First we have to check, wheather current searial number |
|
32 * was already updated on the other nameservers. |
|
33 * If field 'notified_serial' is NULL, then I guess domain is |
|
34 * NATIVE and we don't have any secondary nameservers for this domain. |
|
35 * NOTICE: Serial number *will* be RFC1912 compilant after update |
|
36 * NOTICE: This function will allow only 100 DNS zone transfers ;-) |
|
37 * YYYYMMDDnn |
|
38 */ |
|
39 |
32 |
40 $sqlq = "SELECT notified_serial FROM domains WHERE id = ".$db->quote($domain_id); |
33 $sqlq = "SELECT notified_serial FROM domains WHERE id = ".$db->quote($domain_id); |
41 $notified_serial = $db->queryOne($sqlq); |
34 $notified_serial = $db->queryOne($sqlq); |
42 |
35 |
43 $sqlq = "SELECT content FROM records WHERE type = 'SOA' AND domain_id = ".$db->quote($domain_id); |
36 $sqlq = "SELECT content FROM records WHERE type = 'SOA' AND domain_id = ".$db->quote($domain_id); |
44 $content = $db->queryOne($sqlq); |
37 $content = $db->queryOne($sqlq); |
45 $need_to_update = false; |
38 $need_to_update = false; |
46 |
39 |
47 // Getting the serial field. |
40 // Getting the serial field. |
48 $soa = explode(" ", $content); |
41 $soa = explode(" ", $content); |
49 |
42 |
50 if(empty($notified_serial)) |
43 if(empty($notified_serial)) { |
51 { |
44 // Ok native replication, so we have to update. |
52 // Ok native replication, so we have to update. |
45 $need_to_update = true; |
53 $need_to_update = true; |
46 } elseif($notified_serial >= $soa[2]) { |
54 } |
47 $need_to_update = true; |
55 elseif($notified_serial >= $soa[2]) |
48 } elseif(strlen($soa[2]) != 10) { |
56 { |
49 $need_to_update = true; |
57 $need_to_update = true; |
50 } else { |
58 } |
51 $need_to_update = false; |
59 elseif(strlen($soa[2]) != 10) |
52 } |
60 { |
53 |
61 $need_to_update = true; |
54 if($need_to_update) { |
62 } |
55 // Ok so we have to update it seems. |
63 else |
56 $current_serial = $soa[2]; |
64 { |
|
65 $need_to_update = false; |
|
66 } |
|
67 if($need_to_update) |
|
68 { |
|
69 // Ok so we have to update it seems. |
|
70 $current_serial = $soa[2]; |
|
71 |
|
72 /* |
|
73 * What we need here (for RFC1912) is YEAR, MONTH and DAY |
|
74 * so let's get it ... |
|
75 */ |
|
76 $new_serial = date('Ymd'); // we will add revision number later |
57 $new_serial = date('Ymd'); // we will add revision number later |
77 |
58 |
78 if(strncmp($new_serial, $current_serial, 8) === 0) |
59 if(strncmp($new_serial, $current_serial, 8) === 0) { |
79 { |
60 $revision_number = (int) substr($current_serial, -2); |
80 /* |
61 if ($revision_number == 99) return false; // ok, we cannot update anymore tonight |
81 * Ok, so we already made updates tonight |
62 ++$revision_number; |
82 * let's just increase the revision number |
63 // here it is ... same date, new revision |
83 */ |
64 $new_serial .= str_pad($revision_number, 2, "0", STR_PAD_LEFT); |
84 $revision_number = (int) substr($current_serial, -2); |
65 } else { |
85 if ($revision_number == 99) return false; // ok, we cannot update anymore tonight |
66 /* |
86 ++$revision_number; |
|
87 // here it is ... same date, new revision |
|
88 $new_serial .= str_pad($revision_number, 2, "0", STR_PAD_LEFT); |
|
89 } |
|
90 else |
|
91 { |
|
92 /* |
|
93 * Current serial is not RFC1912 compilant, so let's make a new one |
67 * Current serial is not RFC1912 compilant, so let's make a new one |
94 */ |
68 */ |
95 $new_serial .= '00'; |
69 $new_serial .= '00'; |
96 } |
70 } |
97 $soa[2] = $new_serial; // change serial in SOA array |
71 $soa[2] = $new_serial; // change serial in SOA array |
98 $new_soa = ""; |
72 $new_soa = ""; |
99 // build new soa and update SQL after that |
73 // build new soa and update SQL after that |
100 for ($i = 0; $i < count($soa); $i++) |
74 for ($i = 0; $i < count($soa); $i++) { |
101 { |
|
102 $new_soa .= $soa[$i] . " "; |
75 $new_soa .= $soa[$i] . " "; |
103 } |
76 } |
104 $sqlq = "UPDATE records SET content = ".$db->quote($new_soa)." WHERE domain_id = ".$db->quote($domain_id)." AND type = 'SOA'"; |
77 $sqlq = "UPDATE records SET content = ".$db->quote($new_soa)." WHERE domain_id = ".$db->quote($domain_id)." AND type = 'SOA'"; |
105 $db->Query($sqlq); |
78 $db->Query($sqlq); |
106 return true; |
79 return true; |
110 /* |
83 /* |
111 * Edit a record. |
84 * Edit a record. |
112 * This function validates it if correct it inserts it into the database. |
85 * This function validates it if correct it inserts it into the database. |
113 * return values: true if succesful. |
86 * return values: true if succesful. |
114 */ |
87 */ |
115 function edit_record($recordid, $zoneid, $name, $type, $content, $ttl, $prio) |
88 function edit_record($record) { |
116 { |
89 |
117 global $db; |
90 if (verify_permission(zone_content_edit_others)) { $perm_content_edit = "all" ; } |
118 if($content == "") |
91 elseif (verify_permission(zone_content_edit_own)) { $perm_content_edit = "own" ; } |
119 { |
92 else { $perm_content_edit = "none" ; } |
120 error(ERR_RECORD_EMPTY_CONTENT); |
93 |
121 } |
94 $user_is_zone_owner = verify_user_is_owner_zoneid($record['zid']); |
122 // Edits the given record (validates specific stuff first) |
95 $zone_type = get_domain_type($record['zid']); |
123 if (!xs(recid_to_domid($recordid))) |
96 |
124 { |
97 if ( $zone_type == "SLAVE" || $perm_content_edit == "none" || $perm_content_edit == "own" && $user_is_zone_owner == "0" ) { |
125 error(ERR_RECORD_ACCESS_DENIED); |
98 return _("You are not allowed to edit this record.") ; |
126 } |
99 } else { |
127 if (is_numeric($zoneid)) |
100 if($record['content'] == "") { |
128 { |
101 return _("Error: content field may not be empty.") ; |
129 validate_input($zoneid, $type, $content, $name, $prio, $ttl); |
102 } |
130 $change = time(); |
103 global $db; |
131 $db->query("UPDATE records set name=".$db->quote($name).", type=".$db->quote($type).", content=".$db->quote($content).", ttl=".$db->quote($ttl).", prio=".$db->quote($prio).", change_date=".$db->quote($change)." WHERE id=".$db->quote($recordid)); |
104 // TODO: no need to check for numeric-ness of zone id if we check with validate_input as well? |
132 |
105 if (is_numeric($record['zid'])) { |
133 /* |
106 validate_input($record['zid'], $record['type'], $record['content'], $record['name'], $record['prio'], $record['ttl']); |
134 * Added by DeViCeD - Update SOA Serial number |
107 $query = "UPDATE records |
135 * There should be more checks |
108 SET name=".$db->quote($record['name']).", |
136 */ |
109 type=".$db->quote($record['type']).", |
137 if ($type != 'SOA') |
110 content=".$db->quote($record['content']).", |
|
111 ttl=".$db->quote($record['ttl']).", |
|
112 prio=".$db->quote($record['prio']).", |
|
113 change_date=".$db->quote(time())." |
|
114 WHERE id=".$db->quote($record['rid']); |
|
115 $result = $db->Query($query); |
|
116 if (PEAR::isError($result)) { |
|
117 error($result->getMessage()); |
|
118 return false; |
|
119 } elseif ($record['type'] != 'SOA') { |
|
120 update_soa_serial($record['zid']); |
|
121 } |
|
122 return true; |
|
123 } |
|
124 else |
138 { |
125 { |
139 update_soa_serial($zoneid); |
126 // TODO change to error style as above (returning directly) |
140 } |
127 error(sprintf(ERR_INV_ARGC, "edit_record", "no zoneid given")); |
141 return true; |
128 } |
142 } |
129 } |
143 else |
130 return true; |
144 { |
131 } |
145 error(sprintf(ERR_INV_ARGC, "edit_record", "no zoneid given")); |
132 |
146 } |
|
147 |
|
148 } |
|
149 |
|
150 |
|
151 function add_record_owner($zoneid,$userid,$recordid) |
|
152 { |
|
153 global $db; |
|
154 if (!xs($zoneid)) |
|
155 { |
|
156 error(ERR_RECORD_ACCESS_DENIED); |
|
157 } |
|
158 if (is_numeric($zoneid) || is_numeric($userid) || is_numeric($recordid)) |
|
159 { |
|
160 $db->query("INSERT INTO record_owners (user_id, record_id) VALUES (".$db->quote($userid).", ".$db->quote($recordid).")"); |
|
161 return true; |
|
162 } |
|
163 else |
|
164 { |
|
165 error(sprintf(ERR_INV_ARGC, "add_record_owner", "at least one of the arguments is not numeric")); |
|
166 } |
|
167 } |
|
168 |
|
169 function delete_record_owner($zoneid,$rowid,$recordid) |
|
170 { |
|
171 global $db; |
|
172 if (!xs($zoneid)) |
|
173 { |
|
174 error(ERR_RECORD_ACCESS_DENIED); |
|
175 } |
|
176 if (is_numeric($zoneid) || is_numeric($rowid) || is_numeric($recordid)) |
|
177 { |
|
178 $db->query("DELETE FROM record_owners WHERE id=".$db->quote($rowid)." AND record_id=".$db->quote($recordid)); |
|
179 return true; |
|
180 } |
|
181 else |
|
182 { |
|
183 error(sprintf(ERR_INV_ARGC, "delete_record_owner", "at least one of the arguments is not numeric")); |
|
184 } |
|
185 } |
|
186 |
133 |
187 /* |
134 /* |
188 * Adds a record. |
135 * Adds a record. |
189 * This function validates it if correct it inserts it into the database. |
136 * This function validates it if correct it inserts it into the database. |
190 * return values: true if succesful. |
137 * return values: true if succesful. |
191 */ |
138 */ |
192 function add_record($zoneid, $name, $type, $content, $ttl, $prio) |
139 function add_record($zoneid, $name, $type, $content, $ttl, $prio) { |
193 { |
140 global $db; |
194 |
141 |
195 global $db; |
142 if (verify_permission(zone_content_edit_others)) { $perm_content_edit = "all" ; } |
196 if (!xs($zoneid)) |
143 elseif (verify_permission(zone_content_edit_own)) { $perm_content_edit = "own" ; } |
197 { |
144 else { $perm_content_edit = "none" ; } |
198 error(ERR_RECORD_ACCESS_DENIED); |
145 |
199 } |
146 $user_is_zone_owner = verify_user_is_owner_zoneid($zoneid); |
200 if (is_numeric($zoneid)) |
147 $zone_type = get_domain_type($zoneid); |
201 { |
148 |
202 // Check the user input. |
149 if ( $zone_type == "SLAVE" || $perm_content_edit == "none" || $perm_content_edit == "own" && $user_is_zone_owner == "0" ) { |
203 validate_input($zoneid, $type, $content, $name, $prio, $ttl); |
150 error(ERR_PERM_ADD_RECORD); |
204 |
151 return false; |
205 // Generate new timestamp for the daemon |
152 } else { |
206 $change = time(); |
153 if (validate_input($zoneid, $type, $content, $name, $prio, $ttl) ) { |
207 |
154 $change = time(); |
208 // Execute query. |
155 $query = "INSERT INTO records VALUES (''," |
209 $db->query("INSERT INTO records (domain_id, name, type, content, ttl, prio, change_date) VALUES (".$db->quote($zoneid).", ".$db->quote($name).", ".$db->quote($type).", ".$db->quote($content).", ".$db->quote($ttl).", ".$db->quote($prio).", ".$db->quote($change).")"); |
156 . $db->quote($zoneid) . "," |
210 if ($type != 'SOA') |
157 . $db->quote($name) . "," |
211 { |
158 . $db->quote($type) . "," |
212 update_soa_serial($zoneid); |
159 . $db->quote($content) . "," |
|
160 . $db->quote($ttl) . "," |
|
161 . $db->quote($prio) . "," |
|
162 . $db->quote($change) . ")"; |
|
163 $response = $db->query($query); |
|
164 if (PEAR::isError($response)) { |
|
165 error($response->getMessage()); |
|
166 return false; |
|
167 } else { |
|
168 if ($type != 'SOA') { update_soa_serial($zoneid); } |
|
169 return true; |
|
170 } |
|
171 } else { |
|
172 return false; |
213 } |
173 } |
214 return true; |
174 return true; |
215 } |
175 } |
216 else |
|
217 { |
|
218 error(sprintf(ERR_INV_ARG, "add_record")); |
|
219 } |
|
220 } |
176 } |
221 |
177 |
222 |
178 |
223 function add_supermaster($master_ip, $ns_name, $account) |
179 function add_supermaster($master_ip, $ns_name, $account) |
224 { |
180 { |
225 global $db; |
181 global $db; |
226 if (!is_valid_ip($master_ip) && !is_valid_ip6($master_ip)) |
182 if (!is_valid_ip($master_ip) && !is_valid_ip6($master_ip)) { |
227 { |
183 error(ERR_DNS_IP); |
228 error(sprintf(ERR_INV_ARGC, "add_supermaster", "No or no valid ipv4 or ipv6 address given.")); |
184 return false; |
229 } |
185 } |
230 if (!is_valid_hostname($ns_name)) |
186 if (!is_valid_hostname($ns_name)) { |
231 { |
|
232 error(ERR_DNS_HOSTNAME); |
187 error(ERR_DNS_HOSTNAME); |
233 } |
188 return false; |
234 if (!validate_account($account)) |
189 } |
235 { |
190 if (!validate_account($account)) { |
236 error(sprintf(ERR_INV_ARGC, "add_supermaster", "given account name is invalid (alpha chars only)")); |
191 error(sprintf(ERR_INV_ARGC, "add_supermaster", "given account name is invalid (alpha chars only)")); |
237 } |
192 return false; |
238 if (supermaster_exists($master_ip)) |
193 } |
239 { |
194 if (supermaster_exists($master_ip)) { |
240 error(sprintf(ERR_INV_ARGC, "add_supermaster", "supermaster already exists")); |
195 error(ERR_SM_EXISTS); |
241 } |
196 return false; |
242 else |
197 } else { |
243 { |
|
244 $db->query("INSERT INTO supermasters VALUES (".$db->quote($master_ip).", ".$db->quote($ns_name).", ".$db->quote($account).")"); |
198 $db->query("INSERT INTO supermasters VALUES (".$db->quote($master_ip).", ".$db->quote($ns_name).", ".$db->quote($account).")"); |
245 return true; |
199 return true; |
246 } |
200 } |
247 } |
201 } |
248 |
202 |
249 function delete_supermaster($master_ip) |
203 function delete_supermaster($master_ip) { |
250 { |
204 global $db; |
251 global $db; |
|
252 if (!level(5)) |
|
253 { |
|
254 error(ERR_LEVEL_5); |
|
255 } |
|
256 if (is_valid_ip($master_ip) || is_valid_ip6($master_ip)) |
205 if (is_valid_ip($master_ip) || is_valid_ip6($master_ip)) |
257 { |
206 { |
258 $db->query("DELETE FROM supermasters WHERE ip = ".$db->quote($master_ip)); |
207 $db->query("DELETE FROM supermasters WHERE ip = ".$db->quote($master_ip)); |
259 return true; |
208 return true; |
260 } |
209 } |
287 { |
232 { |
288 error(sprintf(ERR_INV_ARGC, "get_supermaster_info_from_ip", "No or no valid ipv4 or ipv6 address given.")); |
233 error(sprintf(ERR_INV_ARGC, "get_supermaster_info_from_ip", "No or no valid ipv4 or ipv6 address given.")); |
289 } |
234 } |
290 } |
235 } |
291 |
236 |
|
237 function get_record_details_from_record_id($rid) { |
|
238 |
|
239 global $db; |
|
240 |
|
241 $query = "SELECT * FROM records WHERE id = " . $db->quote($rid) ; |
|
242 |
|
243 $response = $db->query($query); |
|
244 if (PEAR::isError($response)) { error($response->getMessage()); return false; } |
|
245 |
|
246 while ($r = $response->fetchRow()) { |
|
247 $return[] = array( |
|
248 "rid" => $r['id'], |
|
249 "zid" => $r['domain_id'], |
|
250 "name" => $r['name'], |
|
251 "type" => $r['type'], |
|
252 "content" => $r['content'], |
|
253 "ttl" => $r['ttl'], |
|
254 "prio" => $r['prio'], |
|
255 "change_date" => $r['change_date']); |
|
256 } |
|
257 return $return; |
|
258 } |
292 |
259 |
293 /* |
260 /* |
294 * Delete a record by a given id. |
261 * Delete a record by a given id. |
295 * return values: true, this function is always succesful. |
262 * return values: true, this function is always succesful. |
296 */ |
263 */ |
297 function delete_record($id) |
264 function delete_record($rid) |
298 { |
265 { |
299 global $db; |
266 global $db; |
300 |
267 |
301 // Check if the user has access. |
268 if (verify_permission(zone_content_edit_others)) { $perm_content_edit = "all" ; } |
302 if (!xs(recid_to_domid($id))) |
269 elseif (verify_permission(zone_content_edit_own)) { $perm_content_edit = "own" ; } |
303 { |
270 else { $perm_content_edit = "none" ; } |
304 error(ERR_RECORD_ACCESS_DENIED); |
271 |
305 } |
272 // Determine ID of zone first. |
306 |
273 $record = get_record_details_from_record_id($rid); |
307 // Retrieve the type of record to see if we can actually remove it. |
274 $user_is_zone_owner = verify_user_is_owner_zoneid($record['zid']); |
308 $recordtype = get_recordtype_from_id($id); |
275 |
309 |
276 if ( $perm_content_edit == "all" || ($perm_content_edit == "own" && $user_is_zone_owner == "0" )) { |
310 // If the record type is NS and the user tries to delete it while ALLOW_NS_EDIT is set to 0 |
277 if ($record['type'] == "SOA") { |
311 // OR |
278 error(_('You are trying to delete the SOA record. If are not allowed to remove it, unless you remove the entire zone.')); |
312 // check if the name of the record isnt the domain name (if so it should delete all records) |
279 } else { |
313 // OR |
280 $quote = "DELETE FROM records WHERE id = " . $db->quote($rid); |
314 // check if we are dealing with a SOA field (same story as NS) |
281 $response = $db->query($query); |
315 if (($recordtype == "NS" && $GLOBALS["ALLOW_NS_EDIT"] != 1 && (get_name_from_record_id($id) == get_domain_name_from_id(recid_to_domid($id)))) || ($recordtype == "SOA" && $GLOBALS["ALLOW_SOA_EDIT"] != 1)) |
282 if (PEAR::isError($response)) { error($response->getMessage()); return false; } |
316 { |
283 return true; |
317 error(sprintf(ERR_RECORD_DELETE_TYPE_DENIED, $recordtype)); |
284 } |
318 |
285 } else { |
319 } |
286 error(ERR_PERM_DEL_RECORD); |
320 if (is_numeric($id)) |
287 return false; |
321 { |
|
322 $did = recid_to_domid($id); |
|
323 $db->query('DELETE FROM records WHERE id=' . $db->quote($id) ); |
|
324 if ($type != 'SOA') |
|
325 { |
|
326 update_soa_serial($did); |
|
327 } |
|
328 // $id doesnt exist in database anymore so its deleted or just not there which means "true" |
|
329 return true; |
|
330 } |
|
331 else |
|
332 { |
|
333 error(sprintf(ERR_INV_ARG, "delete_record")); |
|
334 } |
288 } |
335 } |
289 } |
336 |
290 |
337 |
291 |
338 /* |
292 /* |
346 * remember to request nextID's from the database to be able to insert record. |
300 * remember to request nextID's from the database to be able to insert record. |
347 * if anything is invalid the function will error |
301 * if anything is invalid the function will error |
348 */ |
302 */ |
349 function add_domain($domain, $owner, $webip, $mailip, $empty, $type, $slave_master) |
303 function add_domain($domain, $owner, $webip, $mailip, $empty, $type, $slave_master) |
350 { |
304 { |
351 |
305 if(verify_permission(zone_master_add)) { $zone_master_add = "1" ; } ; |
352 global $db; |
306 if(verify_permission(zone_slave_add)) { $zone_slave_add = "1" ; } ; |
353 |
307 |
354 if (!level(5)) |
308 // TODO: make sure only one is possible if only one is enabled |
355 { |
309 if($zone_master_add == "1" || $zone_master_add == "1") { |
356 error(ERR_LEVEL_5); |
310 |
357 } |
311 global $db; |
358 |
312 if (($domain && $owner && $webip && $mailip) || |
359 // If domain, owner and mailip are given |
313 ($empty && $owner && $domain) || |
360 // OR |
314 (eregi('in-addr.arpa', $domain) && $owner) || |
361 // empty is given and owner and domain |
315 $type=="SLAVE" && $domain && $owner && $slave_master) { |
362 // OR |
316 |
363 // the domain is an arpa record and owner is given |
317 $response = $db->query("INSERT INTO domains (name, type) VALUES (".$db->quote($domain).", ".$db->quote($type).")"); |
364 // OR |
318 if (PEAR::isError($response)) { error($response->getMessage()); return false; } |
365 // the type is slave, domain, owner and slave_master are given |
319 |
366 // THAN |
320 $domain_id = $db->lastInsertId('domains', 'id'); |
367 // Continue this function |
321 if (PEAR::isError($domain_id)) { error($id->getMessage()); return false; } |
368 if (($domain && $owner && $webip && $mailip) || ($empty && $owner && $domain) || (eregi('in-addr.arpa', $domain) && $owner) || $type=="SLAVE" && $domain && $owner && $slave_master) |
322 |
369 { |
323 $response = $db->query("INSERT INTO zones (domain_id, owner) VALUES (".$db->quote($domain_id).", ".$db->quote($owner).")"); |
370 // First insert zone into domain table |
324 if (PEAR::isError($response)) { error($response->getMessage()); return false; } |
371 $db->query("INSERT INTO domains (name, type) VALUES (".$db->quote($domain).", ".$db->quote($type).")"); |
325 |
372 |
326 if ($type == "SLAVE") { |
373 // Determine id of insert zone (in other words, find domain_id) |
327 $response = $db->query("UPDATE domains SET master = ".$db->quote($slave_master)." WHERE id = ".$db->quote($domain_id)); |
374 $iddomain = $db->lastInsertId('domains', 'id'); |
328 if (PEAR::isError($response)) { error($response->getMessage()); return false; } |
375 if (PEAR::isError($iddomain)) { |
|
376 die($id->getMessage()); |
|
377 } |
|
378 |
|
379 // Second, insert into zones tables |
|
380 $db->query("INSERT INTO zones (domain_id, owner) VALUES (".$db->quote($iddomain).", ".$db->quote($owner).")"); |
|
381 |
|
382 if ($type == "SLAVE") |
|
383 { |
|
384 $db->query("UPDATE domains SET master = ".$db->quote($slave_master)." WHERE id = ".$db->quote($iddomain)); |
|
385 |
|
386 // Done |
|
387 return true; |
|
388 } |
|
389 else |
|
390 { |
|
391 // Generate new timestamp. We need this one anyhow. |
|
392 $now = time(); |
|
393 |
|
394 if ($empty && $iddomain) |
|
395 { |
|
396 // If we come into this if statement we dont want to apply templates. |
|
397 // Retrieve configuration settings. |
|
398 $ns1 = $GLOBALS["NS1"]; |
|
399 $hm = $GLOBALS["HOSTMASTER"]; |
|
400 $ttl = $GLOBALS["DEFAULT_TTL"]; |
|
401 |
|
402 // Build and execute query |
|
403 $sql = "INSERT INTO records (domain_id, name, content, type, ttl, prio, change_date) VALUES (".$db->quote($iddomain).", ".$db->quote($domain).", ".$db->quote($ns1.' '.$hm.' 1').", 'SOA', ".$db->quote($ttl).", 0, ".$db->quote($now).")"; |
|
404 $db->query($sql); |
|
405 |
|
406 // Done |
|
407 return true; |
329 return true; |
|
330 } else { |
|
331 $now = time(); |
|
332 if ($empty && $domain_id) { |
|
333 $ns1 = $GLOBALS['NS1']; |
|
334 $hm = $GLOBALS['HOSTMASTER']; |
|
335 $ttl = $GLOBALS['DEFAULT_TTL']; |
|
336 |
|
337 $query = "INSERT INTO records VALUES (''," |
|
338 . $db->quote($domain_id) . "," |
|
339 . $db->quote($domain) . "," |
|
340 . $db->quote($ns1.' '.$hm.' 1') |
|
341 . ",'SOA'," |
|
342 . $db->quote($ttl) |
|
343 . ", 0, " |
|
344 . $db->quote($now).")"; |
|
345 $response = $db->query($query); |
|
346 if (PEAR::isError($response)) { error($response->getMessage()); return false; } |
|
347 } elseif ($domain_id) { |
|
348 global $template; |
|
349 |
|
350 foreach ($template as $r) { |
|
351 if ((eregi('in-addr.arpa', $domain) && ($r["type"] == "NS" || $r["type"] == "SOA")) || (!eregi('in-addr.arpa', $domain))) |
|
352 { |
|
353 $name = parse_template_value($r["name"], $domain, $webip, $mailip); |
|
354 $type = $r["type"]; |
|
355 $content = parse_template_value($r["content"], $domain, $webip, $mailip); |
|
356 $ttl = $r["ttl"]; |
|
357 $prio = intval($r["prio"]); |
|
358 |
|
359 if (!$ttl) { |
|
360 $ttl = $GLOBALS["DEFAULT_TTL"]; |
|
361 } |
|
362 |
|
363 $query = "INSERT INTO records VALUES (''," |
|
364 . $db->quote($domain_id) . "," |
|
365 . $db->quote($name) . "," |
|
366 . $db->quote($content) . "," |
|
367 . $db->quote($type) . "," |
|
368 . $db->quote($ttl) . "," |
|
369 . $db->quote($prio) . "," |
|
370 . $db->quote($now) . ")"; |
|
371 $response = $db->query($query); |
|
372 if (PEAR::isError($response)) { error($response->getMessage()); return false; } |
|
373 } |
|
374 } |
|
375 return true; |
|
376 } else { |
|
377 error(sprintf(ERR_INV_ARGC, "add_domain", "could not create zone")); |
|
378 } |
408 } |
379 } |
409 elseif ($iddomain) |
380 } else { |
410 { |
381 error(sprintf(ERR_INV_ARG, "add_domain")); |
411 // If we are here we want to apply templates. |
382 } |
412 global $template; |
383 } else { |
413 |
384 error(ERR_PERM_ADD_ZONE_MASTER); |
414 // Iterate over the template and apply it for each field. |
385 return false; |
415 foreach ($template as $r) |
|
416 { |
|
417 // Same type of if statement as previous. |
|
418 if ((eregi('in-addr.arpa', $domain) && ($r["type"] == "NS" || $r["type"] == "SOA")) || (!eregi('in-addr.arpa', $domain))) |
|
419 { |
|
420 // Parse the template. |
|
421 $name = parse_template_value($r["name"], $domain, $webip, $mailip); |
|
422 $type = $r["type"]; |
|
423 $content = parse_template_value($r["content"], $domain, $webip, $mailip); |
|
424 $ttl = $r["ttl"]; |
|
425 $prio = intval($r["prio"]); |
|
426 |
|
427 // If no ttl is given, use the default. |
|
428 if (!$ttl) |
|
429 { |
|
430 $ttl = $GLOBALS["DEFAULT_TTL"]; |
|
431 } |
|
432 |
|
433 $sql = "INSERT INTO records (domain_id, name, content, type, ttl, prio, change_date) VALUES (".$db->quote($iddomain).", ".$db->quote($name).", ".$db->quote($content).", ".$db->quote($type).", ".$db->quote($ttl).", ".$db->quote($prio).", ".$db->quote($now).")"; |
|
434 $db->query($sql); |
|
435 } |
|
436 } |
|
437 // All done. |
|
438 return true; |
|
439 } |
|
440 else |
|
441 { |
|
442 error(sprintf(ERR_INV_ARGC, "add_domain", "could not create zone")); |
|
443 } |
|
444 } |
|
445 } |
|
446 else |
|
447 { |
|
448 error(sprintf(ERR_INV_ARG, "add_domain")); |
|
449 } |
386 } |
450 } |
387 } |
451 |
388 |
452 |
389 |
453 /* |
390 /* |
499 } |
435 } |
500 } |
436 } |
501 |
437 |
502 |
438 |
503 /* |
439 /* |
504 * Sorts a zone by records. |
440 * Change owner of a domain. |
505 * return values: the sorted zone. |
441 * return values: true when succesful. |
506 */ |
442 */ |
507 function sort_zone($records) |
443 function add_owner_to_zone($zone_id, $user_id) |
508 { |
444 { |
509 $ar_so = array(); |
445 global $db; |
510 $ar_ns = array(); |
446 if ( (verify_permission(zone_meta_edit_others)) || (verify_permission(zone_meta_edit_own)) && verify_user_is_owner_zoneid($_GET["id"])) { |
511 $ar_mx = array(); |
447 // User is allowed to make change to meta data of this zone. |
512 $ar_mb = array(); |
448 if (is_numeric($zone_id) && is_numeric($user_id) && is_valid_user($user_id)) |
513 $ar_ur = array(); |
|
514 $ar_ov = array(); |
|
515 foreach ($records as $c) |
|
516 { |
|
517 switch(strtoupper($c['type'])) |
|
518 { |
449 { |
519 case "SOA": |
450 if($db->queryOne("SELECT COUNT(id) FROM zones WHERE owner=".$db->quote($user_id)." AND domain_id=".$db->quote($zone_id)) == 0) |
520 $ar_so[] = $c; |
451 { |
521 break; |
452 $db->query("INSERT INTO zones (domain_id, owner) VALUES(".$db->quote($zone_id).", ".$db->quote($user_id).")"); |
522 case "NS": |
453 } |
523 $ar_ns[] = $c; |
454 return true; |
524 break; |
455 } else { |
525 case "MX": |
456 error(sprintf(ERR_INV_ARGC, "add_owner_to_zone", "$zone_id / $user_id")); |
526 $ar_mx[] = $c; |
457 } |
527 break; |
458 } else { |
528 case "MBOXFW": |
459 return false; |
529 $ar_mb[] = $c; |
460 } |
530 break; |
461 } |
531 case "URL": |
462 |
532 $ar_ur[] = $c; |
463 |
533 break; |
464 function delete_owner_from_zone($zone_id, $user_id) |
534 default: |
465 { |
535 $ar_ov[] = $c; |
466 global $db; |
536 break; |
467 if ( (verify_permission(zone_meta_edit_others)) || (verify_permission(zone_meta_edit_own)) && verify_user_is_owner_zoneid($_GET["id"])) { |
537 } |
468 // User is allowed to make change to meta data of this zone. |
538 } |
469 if (is_numeric($zone_id) && is_numeric($user_id) && is_valid_user($user_id)) |
539 |
|
540 $res = array_merge($ar_so, $ar_ns, $ar_mx, $ar_mb, $ar_ur, $ar_ov); |
|
541 |
|
542 if (count($records) == count($res)) |
|
543 { |
|
544 $records = $res; |
|
545 } |
|
546 else |
|
547 { |
|
548 error(sprintf(ERR_INV_ARGC, "sort_zone", "records sorting failed!")); |
|
549 } |
|
550 return $records; |
|
551 } |
|
552 |
|
553 |
|
554 /* |
|
555 * Change owner of a domain. |
|
556 * Function should actually be in users.inc.php. But its more of a record modification than a user modification |
|
557 * return values: true when succesful. |
|
558 */ |
|
559 function add_owner($domain, $newowner) |
|
560 { |
|
561 global $db; |
|
562 |
|
563 if (!level(5)) |
|
564 { |
|
565 error(ERR_LEVEL_5); |
|
566 } |
|
567 |
|
568 if (is_numeric($domain) && is_numeric($newowner) && is_valid_user($newowner)) |
|
569 { |
|
570 if($db->queryOne("SELECT COUNT(id) FROM zones WHERE owner=".$db->quote($newowner)." AND domain_id=".$db->quote($domain)) == 0) |
|
571 { |
470 { |
572 $db->query("INSERT INTO zones (domain_id, owner) VALUES(".$db->quote($domain).", ".$db->quote($newowner).")"); |
471 // TODO: Next if() required, why not just execute DELETE query? |
573 } |
472 if($db->queryOne("SELECT COUNT(id) FROM zones WHERE owner=".$db->quote($user_id)." AND domain_id=".$db->quote($zone_id)) != 0) |
574 return true; |
473 { |
575 } |
474 $db->query("DELETE FROM zones WHERE owner=".$db->quote($user_id)." AND domain_id=".$db->quote($zone_id)); |
576 else |
475 } |
577 { |
476 return true; |
578 error(sprintf(ERR_INV_ARGC, "change_owner", "$domain / $newowner")); |
477 } else { |
579 } |
478 error(sprintf(ERR_INV_ARGC, "delete_owner_from_zone", "$zone_id / $user_id")); |
580 } |
479 } |
581 |
480 } else { |
582 |
481 return false; |
583 function delete_owner($domain, $owner) |
482 } |
584 { |
483 |
585 global $db; |
|
586 if($db->queryOne("SELECT COUNT(id) FROM zones WHERE owner=".$db->quote($owner)." AND domain_id=".$db->quote($domain)) != 0) |
|
587 { |
|
588 $db->query("DELETE FROM zones WHERE owner=".$db->quote($owner)." AND domain_id=".$db->quote($domain)); |
|
589 } |
|
590 return true; |
|
591 } |
484 } |
592 |
485 |
593 /* |
486 /* |
594 * Retrieves all supported dns record types |
487 * Retrieves all supported dns record types |
595 * This function might be deprecated. |
488 * This function might be deprecated. |
651 * return values: the name associated with the id. |
544 * return values: the name associated with the id. |
652 */ |
545 */ |
653 function get_name_from_record_id($id) |
546 function get_name_from_record_id($id) |
654 { |
547 { |
655 global $db; |
548 global $db; |
656 if (is_numeric($id)) |
549 if (is_numeric($id)) { |
657 { |
|
658 $result = $db->query("SELECT name FROM records WHERE id=".$db->quote($id)); |
550 $result = $db->query("SELECT name FROM records WHERE id=".$db->quote($id)); |
659 $r = $result->fetchRow(); |
551 $r = $result->fetchRow(); |
660 return $r["name"]; |
552 return $r["name"]; |
661 } |
553 } else { |
662 else |
|
663 { |
|
664 error(sprintf(ERR_INV_ARG, "get_name_from_record_id")); |
554 error(sprintf(ERR_INV_ARG, "get_name_from_record_id")); |
665 } |
|
666 } |
|
667 |
|
668 |
|
669 /* |
|
670 * Get all the domains from a database of which the user is the owner. |
|
671 * return values: an array with the id of the domain and its name. |
|
672 */ |
|
673 function get_domains_from_userid($id) |
|
674 { |
|
675 global $db; |
|
676 if (is_numeric($id)) |
|
677 { |
|
678 $a_zones = array(); |
|
679 |
|
680 // Check for zones the user has full access for (the |
|
681 // user is owner of the zone. |
|
682 |
|
683 $res_full = $db->query("SELECT |
|
684 domains.id AS domain_id, |
|
685 domains.name AS name |
|
686 FROM domains |
|
687 LEFT JOIN zones ON domains.id=zones.domain_id |
|
688 WHERE owner=".$db->quote($id)); |
|
689 |
|
690 // Process the output. |
|
691 |
|
692 $numrows = $res_full->numRows(); |
|
693 $i=1; |
|
694 if ($numrows > 0) |
|
695 { |
|
696 $andnot=" AND NOT domains.id IN ("; |
|
697 while($r = $res_full->fetchRow()) { |
|
698 |
|
699 // Create array of zone id's and name's the owner |
|
700 // has full access to. |
|
701 |
|
702 $a_zones[] = array( |
|
703 "id" => $r["domain_id"], |
|
704 "name" => $r["name"], |
|
705 "partial" => "0" |
|
706 ); |
|
707 |
|
708 // Create AND NOT for query of zones the user has |
|
709 // only partial access to. In that query we just |
|
710 // want to see the zones he has not full access to |
|
711 // as well. |
|
712 |
|
713 $andnot.=$db->quote($r["domain_id"]); |
|
714 if ($i < $numrows) { |
|
715 $andnot.=","; |
|
716 $i++; |
|
717 } |
|
718 |
|
719 } |
|
720 $andnot.=")"; |
|
721 } |
|
722 else |
|
723 { |
|
724 $andnot=""; |
|
725 } |
|
726 |
|
727 // Check for zones the user has partial access only to. |
|
728 |
|
729 $res_partial = $db->query("SELECT DISTINCT |
|
730 records.domain_id, |
|
731 domains.name |
|
732 FROM records, record_owners, domains |
|
733 WHERE record_owners.user_id = ".$db->quote($id)." |
|
734 AND records.id = record_owners.record_id |
|
735 AND domains.id = records.domain_id |
|
736 ".$andnot); |
|
737 |
|
738 // Add these zones to the array as well. |
|
739 |
|
740 while ($r = $res_partial->fetchRow()) |
|
741 { |
|
742 $a_zones[] = array( |
|
743 "id" => $r["domain_id"], |
|
744 "name" => $r["name"], |
|
745 "partial" => "1" |
|
746 ); |
|
747 } |
|
748 |
|
749 return $a_zones; |
|
750 } |
|
751 else |
|
752 { |
|
753 error(sprintf(ERR_INV_ARGC, "get_domains_from_userid", "This is not a valid userid: $id")); |
|
754 } |
555 } |
755 } |
556 } |
756 |
557 |
757 |
558 |
758 /* |
559 /* |
760 * return values: the name of the domain associated with the id. |
561 * return values: the name of the domain associated with the id. |
761 */ |
562 */ |
762 function get_domain_name_from_id($id) |
563 function get_domain_name_from_id($id) |
763 { |
564 { |
764 global $db; |
565 global $db; |
765 if (!xs($id)) |
566 |
766 { |
|
767 error(ERR_RECORD_ACCESS_DENIED); |
|
768 } |
|
769 if (is_numeric($id)) |
567 if (is_numeric($id)) |
770 { |
568 { |
771 $result = $db->query("SELECT name FROM domains WHERE id=".$db->quote($id)); |
569 $result = $db->query("SELECT name FROM domains WHERE id=".$db->quote($id)); |
772 if ($result->numRows() == 1) |
570 $rows = $result->numRows() ; |
773 { |
571 if ($rows == 1) { |
774 $r = $result->fetchRow(); |
572 $r = $result->fetchRow(); |
775 return $r["name"]; |
573 return $r["name"]; |
776 } |
574 } elseif ($rows == "0") { |
777 else |
575 error(sprintf("Zone does not exist.")); |
778 { |
576 return false; |
|
577 } else { |
779 error(sprintf(ERR_INV_ARGC, "get_domain_name_from_id", "more than one domain found?! whaaa! BAD! BAD! Contact admin!")); |
578 error(sprintf(ERR_INV_ARGC, "get_domain_name_from_id", "more than one domain found?! whaaa! BAD! BAD! Contact admin!")); |
|
579 return false; |
780 } |
580 } |
781 } |
581 } |
782 else |
582 else |
783 { |
583 { |
784 error(sprintf(ERR_INV_ARGC, "get_domain_name_from_id", "Not a valid domainid: $id")); |
584 error(sprintf(ERR_INV_ARGC, "get_domain_name_from_id", "Not a valid domainid: $id")); |
785 } |
585 } |
786 } |
586 } |
787 |
587 |
788 |
588 function get_zone_info_from_id($zone_id) { |
789 /* |
589 |
790 * Get information about a domain name from a given domain id. |
590 if (verify_permission(zone_content_view_others)) { $perm_view = "all" ; } |
791 * the function looks up the domainname, the owner of the domain and the number of records in it. |
591 elseif (verify_permission(zone_content_view_own)) { $perm_view = "own" ; } |
792 * return values: an array containing the information. |
592 else { $perm_view = "none" ;} |
793 */ |
593 |
794 function get_domain_info_from_id($id) |
594 if ($perm_view == "none") { |
795 { |
595 error(ERR_PERM_VIEW_ZONE); |
796 global $db; |
596 } else { |
797 if (!xs($id)) |
597 global $db; |
798 { |
598 |
799 error(ERR_RECORD_ACCESS_DENIED); |
599 $query = "SELECT domains.type AS type, |
800 } |
600 domains.name AS name, |
801 if (is_numeric($id)) |
601 domains.master AS master_ip, |
802 { |
602 count(records.domain_id) AS record_count |
803 |
603 FROM domains, records |
804 if ($_SESSION[$id."_ispartial"] == 1) { |
604 WHERE domains.id = " . $db->quote($zone_id) . " |
805 |
605 AND domains.id = records.domain_id |
806 $sqlq = "SELECT |
606 GROUP BY domains.id"; |
807 domains.type AS type, |
607 |
808 domains.name AS name, |
608 $response = $db->queryRow($query); |
809 users.fullname AS owner, |
609 if (PEAR::isError($response)) { error($response->getMessage()); return false; } |
810 count(record_owners.id) AS aantal |
610 $return = array( |
811 FROM domains, users, record_owners, records |
611 "name" => $response['name'], |
812 |
612 "type" => $response['type'], |
813 WHERE record_owners.user_id = ".$db->quote($_SESSION["userid"])." |
613 "master_ip" => $response['master_ip'], |
814 AND record_owners.record_id = records.id |
614 "record_count" => $response['record_count']); |
815 AND records.domain_id = ".$db->quote($id)." |
615 return $return; |
816 |
|
817 GROUP BY domains.name, owner, users.fullname, domains.type |
|
818 ORDER BY domains.name"; |
|
819 |
|
820 $result = $db->queryRow($sqlq); |
|
821 |
|
822 $ret = array( |
|
823 "name" => $result["name"], |
|
824 "ownerid" => $_SESSION["userid"], |
|
825 "owner" => $result["owner"], |
|
826 "type" => $result["type"], |
|
827 "numrec" => $result["aantal"] |
|
828 ); |
|
829 |
|
830 return $ret; |
|
831 |
|
832 } else{ |
|
833 |
|
834 // Query that retrieves the information we need. |
|
835 $sqlq = "SELECT |
|
836 domains.type AS type, |
|
837 domains.name AS name, |
|
838 min(zones.owner) AS ownerid, |
|
839 users.fullname AS owner, |
|
840 count(records.domain_id) AS aantal |
|
841 FROM domains |
|
842 LEFT JOIN records ON domains.id=records.domain_id |
|
843 LEFT JOIN zones ON domains.id=zones.domain_id |
|
844 LEFT JOIN users ON zones.owner=users.id |
|
845 WHERE domains.id=$id |
|
846 GROUP BY domains.name, owner, users.fullname, domains.type, zones.id |
|
847 ORDER BY zones.id"; |
|
848 |
|
849 // Put the first occurence in an array and return it. |
|
850 $result = $db->queryRow($sqlq); |
|
851 |
|
852 //$result["ownerid"] = ($result["ownerid"] == NULL) ? $db->queryOne("select min(id) from users where users.level=10") : $result["ownerid"]; |
|
853 |
|
854 $ret = array( |
|
855 "name" => $result["name"], |
|
856 "ownerid" => $result["ownerid"], |
|
857 "owner" => $result["owner"], |
|
858 "type" => $result["type"], |
|
859 "numrec" => $result["aantal"] |
|
860 ); |
|
861 return $ret; |
|
862 } |
|
863 |
|
864 } |
|
865 else |
|
866 { |
|
867 error(sprintf(ERR_INV_ARGC, "get_domain_num_records_from_id", "This is not a valid domainid: $id")); |
|
868 } |
616 } |
869 } |
617 } |
870 |
618 |
871 |
619 |
872 /* |
620 /* |
947 error(sprintf(ERR_INV_ARGC, "supermaster_exists", "No or no valid IPv4 or IPv6 address given.")); |
680 error(sprintf(ERR_INV_ARGC, "supermaster_exists", "No or no valid IPv4 or IPv6 address given.")); |
948 } |
681 } |
949 } |
682 } |
950 |
683 |
951 |
684 |
952 /* |
685 function get_zones($perm,$userid=0,$letterstart=all,$rowstart=0,$rowamount=999999) |
953 * Get all domains from the database |
|
954 * This function gets all the domains from the database unless a user id is below 5. |
|
955 * if a user id is below 5 this function will only retrieve records for that user. |
|
956 * return values: the array of domains or -1 if nothing is found. |
|
957 */ |
|
958 function get_domains($userid=true,$letterstart='all',$rowstart=0,$rowamount=999999) |
|
959 { |
686 { |
960 global $db; |
687 global $db; |
961 global $sql_regexp; |
688 global $sql_regexp; |
962 if((!level(5) || !$userid) && !level(10) && !level(5)) |
689 if ($perm != "own" && $perm != "all") { |
963 { |
690 error(ERR_PERM_VIEW_ZONE); |
964 $add = " AND zones.owner=".$db->quote($_SESSION["userid"]); |
691 return false; |
965 } |
692 } |
966 else |
693 else |
967 { |
694 { |
968 $add = ""; |
695 if ($perm == "own") { |
969 } |
696 $sql_add = " AND zones.domain_id = domains.id |
970 |
697 AND zones.owner = ".$db->quote($userid); |
971 $sqlq = "SELECT domains.id AS domain_id, |
698 } |
972 min(zones.owner) AS owner, |
699 if ($letterstart!=all && $letterstart!=1) { |
973 count(DISTINCT records.id) AS aantal, |
700 $sql_add .=" AND domains.name LIKE ".$db->quote($letterstart."%")." "; |
974 domains.name AS domainname |
701 } elseif ($letterstart==1) { |
975 FROM domains |
702 $sql_add .=" AND substring(domains.name,1,1) ".$sql_regexp." '^[[:digit:]]'"; |
976 LEFT JOIN zones ON domains.id=zones.domain_id |
703 } |
977 LEFT JOIN records ON records.domain_id=domains.id |
704 } |
978 WHERE 1=1 $add "; |
705 |
979 if ($letterstart!='all' && $letterstart!=1) { |
706 $sqlq = "SELECT domains.id, |
980 $sqlq.=" AND substring(domains.name,1,1) ".$sql_regexp." ".$db->quote("^".$letterstart); |
707 domains.name, |
981 } elseif ($letterstart==1) { |
708 domains.type, |
982 $sqlq.=" AND substring(domains.name,1,1) ".$sql_regexp." '^[[:digit:]]'"; |
709 COUNT(DISTINCT records.id) AS count_records |
983 } |
710 FROM domains |
984 $sqlq.=" GROUP BY domainname, domains.id |
711 LEFT JOIN zones ON domains.id=zones.domain_id |
985 ORDER BY domainname"; |
712 LEFT JOIN records ON records.domain_id=domains.id |
986 |
713 WHERE 1=1".$sql_add." |
|
714 GROUP BY domains.name, domains.id |
|
715 ORDER BY domains.name"; |
|
716 |
987 $db->setLimit($rowamount, $rowstart); |
717 $db->setLimit($rowamount, $rowstart); |
988 $result = $db->query($sqlq); |
718 $result = $db->query($sqlq); |
989 // Set limit needs to be called before each query |
|
990 $db->setLimit($rowamount, $rowstart); |
|
991 $result2 = $db->query($sqlq); |
|
992 |
|
993 $numrows = $result2->numRows(); |
|
994 $i=1; |
|
995 if ($numrows > 0) { |
|
996 $andnot=" AND NOT domains.id IN ("; |
|
997 while($r = $result2->fetchRow()) { |
|
998 $andnot.=$db->quote($r["domain_id"]); |
|
999 if ($i < $numrows) { |
|
1000 $andnot.=","; |
|
1001 $i++; |
|
1002 } |
|
1003 } |
|
1004 $andnot.=")"; |
|
1005 } |
|
1006 else |
|
1007 { |
|
1008 $andnot=""; |
|
1009 } |
|
1010 |
|
1011 if ($letterstart!='all' && $letterstart!=1) { |
|
1012 |
|
1013 $sqlq = "SELECT domains.id AS domain_id, |
|
1014 count(DISTINCT record_owners.record_id) AS aantal, |
|
1015 domains.name AS domainname |
|
1016 FROM domains, record_owners,records, zones |
|
1017 WHERE record_owners.user_id = ".$db->quote($_SESSION["userid"])." |
|
1018 AND (records.id = record_owners.record_id |
|
1019 AND domains.id = records.domain_id) |
|
1020 $andnot |
|
1021 AND domains.name LIKE ".$db->quote($letterstart."%")." |
|
1022 AND (zones.domain_id != records.domain_id AND zones.owner!=".$db->quote($_SESSION["userid"]).") |
|
1023 GROUP BY domainname, domains.id |
|
1024 ORDER BY domainname"; |
|
1025 |
|
1026 $result_extra = $db->query($sqlq); |
|
1027 |
|
1028 } else { |
|
1029 |
|
1030 $sqlq = "SELECT domains.id AS domain_id, |
|
1031 count(DISTINCT record_owners.record_id) AS aantal, |
|
1032 domains.name AS domainname |
|
1033 FROM domains, record_owners,records, zones |
|
1034 WHERE record_owners.user_id = ".$db->quote($_SESSION["userid"])." |
|
1035 AND (records.id = record_owners.record_id |
|
1036 AND domains.id = records.domain_id) |
|
1037 $andnot"; |
|
1038 if ($letterstart != 'all') { |
|
1039 $sqlq .= " AND substring(domains.name,1,1) ".$sql_regexp." '^[[:digit:]]'"; |
|
1040 } |
|
1041 $sqlq .= "AND (zones.domain_id != records.domain_id AND zones.owner!=".$db->quote($_SESSION["userid"]).") |
|
1042 GROUP BY domainname, domains.id |
|
1043 ORDER BY domainname"; |
|
1044 |
|
1045 $result_extra[$i] = $db->query($sqlq); |
|
1046 |
|
1047 } |
|
1048 |
719 |
1049 while($r = $result->fetchRow()) |
720 while($r = $result->fetchRow()) |
1050 { |
721 { |
1051 $r["owner"] = ($r["owner"] == NULL) ? $db->queryOne("select min(id) from users where users.level=10") : $r["owner"]; |
722 $ret[$r["name"]] = array( |
1052 $ret[$r["domainname"]] = array( |
723 "id" => $r["id"], |
1053 "name" => $r["domainname"], |
724 "name" => $r["name"], |
1054 "id" => $r["domain_id"], |
725 "type" => $r["type"], |
1055 "owner" => $r["owner"], |
726 "count_records" => $r["count_records"] |
1056 "numrec" => $r["aantal"] |
727 ); |
1057 ); |
728 } |
1058 } |
729 return $ret; |
1059 |
730 } |
1060 |
731 |
1061 if ($letterstart!='all' && $letterstart!=1) { |
732 // TODO: letterstart limitation and userid permission limitiation should be applied at the same time? |
1062 |
733 function zone_count_ng($perm, $letterstart=all) { |
1063 while($r = $result_extra->fetchRow()) |
734 global $db; |
1064 { |
|
1065 $ret[$r["domainname"]] = array( |
|
1066 "name" => $r["domainname"]."*", |
|
1067 "id" => $r["domain_id"], |
|
1068 "owner" => $_SESSION["userid"], |
|
1069 "numrec" => $r["aantal"] |
|
1070 ); |
|
1071 $_SESSION["partial_".$r["domainname"]] = 1; |
|
1072 } |
|
1073 |
|
1074 } else { |
|
1075 |
|
1076 foreach ($result_extra as $result_e) { |
|
1077 while($r = $result_e->fetchRow()) |
|
1078 { |
|
1079 $ret[$r["domainname"]] = array( |
|
1080 "name" => $r["domainname"]."*", |
|
1081 "id" => $r["domain_id"], |
|
1082 "owner" => $_SESSION["userid"], |
|
1083 "numrec" => $r["aantal"] |
|
1084 ); |
|
1085 $_SESSION["partial_".$r["domainname"]] = 1; |
|
1086 } |
|
1087 } |
|
1088 |
|
1089 } |
|
1090 |
|
1091 if (empty($ret)) { |
|
1092 return -1; |
|
1093 } else { |
|
1094 sort($ret); |
|
1095 return $ret; |
|
1096 } |
|
1097 |
|
1098 } |
|
1099 |
|
1100 |
|
1101 /* |
|
1102 * zone_count |
|
1103 * Does a select query to count how many zones we have in the database |
|
1104 * |
|
1105 * @todo: see whether or not it is possible to add the records |
|
1106 * @param $userid integer The userid of the current user |
|
1107 * @return integer the number of zones |
|
1108 */ |
|
1109 |
|
1110 function zone_count($userid=true, $letterstart='all') { |
|
1111 global $db; |
|
1112 global $sql_regexp; |
735 global $sql_regexp; |
1113 if((!level(5) || !$userid) && !level(10) && !level(5)) |
736 if ($perm != "own" && $perm != "all") { |
1114 { |
737 $zone_count = "0"; |
1115 // First select the zones for which we have ownership on one or more records. |
738 } |
1116 $query = 'SELECT records.domain_id FROM records, record_owners WHERE user_id = '.$db->quote($_SESSION['userid']).' AND records.id = record_owners.record_id'; |
739 else |
1117 $result = $db->query($query); |
740 { |
1118 $zones = array(); |
741 if ($perm == "own") { |
1119 if (!PEAR::isError($result)) { |
742 $sql_add = " AND zones.domain_id = domains.id |
1120 $zones = $result->fetchCol(); |
743 AND zones.owner = ".$db->quote($_SESSION['userid']); |
1121 } |
744 } |
1122 |
745 if ($letterstart!=all && $letterstart!=1) { |
1123 $add = " AND (zones.owner=".$db->quote($_SESSION["userid"]); |
746 $sql_add .=" AND domains.name LIKE ".$db->quote($letterstart."%")." "; |
1124 if (count($zones) > 0) { |
747 } elseif ($letterstart==1) { |
1125 $add .= ' OR zones.domain_id IN ('.implode(',', $zones).') '; |
748 $sql_add .=" AND substring(domains.name,1,1) ".$sql_regexp." '^[[:digit:]]'"; |
1126 |
749 } |
1127 } |
750 |
1128 $add .= ')'; |
751 $sqlq = "SELECT COUNT(distinct domains.id) AS count_zones |
1129 } |
752 FROM domains,zones |
1130 else |
753 WHERE 1=1 |
1131 { |
754 ".$sql_add.";"; |
1132 $add = ""; |
755 |
1133 } |
756 $zone_count = $db->queryOne($sqlq); |
1134 |
757 } |
1135 if ($letterstart!='all' && $letterstart!=1) { |
758 return $zone_count; |
1136 $add .=" AND domains.name LIKE ".$db->quote($letterstart."%")." "; |
759 } |
1137 } elseif ($letterstart==1) { |
760 |
1138 $add .=" AND substring(domains.name,1,1) ".$sql_regexp." '^[[:digit:]]'"; |
761 function zone_count_for_uid($uid) { |
1139 } |
762 global $db; |
1140 |
763 $query = "SELECT COUNT(domain_id) |
1141 if (level(5)) |
764 FROM zones |
1142 { |
765 WHERE owner = " . $db->quote($uid) . " |
1143 $query = 'SELECT count(distinct domains.id) as zone_count FROM domains WHERE 1=1 '.$add; |
766 ORDER BY domain_id"; |
1144 } |
767 $zone_count = $db->queryOne($query); |
1145 else |
768 return $zone_count; |
1146 { |
769 } |
1147 $query = 'SELECT count(distinct zones.domain_id) as zone_count FROM zones, domains WHERE zones.domain_id = domains.id '.$add; |
770 |
1148 } |
|
1149 $numRows = $db->queryOne($query); |
|
1150 return $numRows; |
|
1151 } |
|
1152 |
771 |
1153 /* |
772 /* |
1154 * Get a record from an id. |
773 * Get a record from an id. |
1155 * Retrieve all fields of the record and send it back to the function caller. |
774 * Retrieve all fields of the record and send it back to the function caller. |
1156 * return values: the array with information, or -1 is nothing is found. |
775 * return values: the array with information, or -1 is nothing is found. |
1195 /* |
814 /* |
1196 * Get all records from a domain id. |
815 * Get all records from a domain id. |
1197 * Retrieve all fields of the records and send it back to the function caller. |
816 * Retrieve all fields of the records and send it back to the function caller. |
1198 * return values: the array with information, or -1 is nothing is found. |
817 * return values: the array with information, or -1 is nothing is found. |
1199 */ |
818 */ |
1200 function get_records_from_domain_id($id,$rowstart=0,$rowamount=999999) |
819 function get_records_from_domain_id($id,$rowstart=0,$rowamount=999999) { |
1201 { |
820 global $db; |
1202 global $db; |
821 if (is_numeric($id)) { |
1203 if (is_numeric($id)) |
|
1204 { |
|
1205 if ($_SESSION[$id."_ispartial"] == 1) { |
822 if ($_SESSION[$id."_ispartial"] == 1) { |
1206 $db->setLimit($rowamount, $rowstart); |
823 $db->setLimit($rowamount, $rowstart); |
1207 $result = $db->query("SELECT record_owners.record_id as id |
824 $result = $db->query("SELECT record_owners.record_id as id |
1208 FROM record_owners,domains,records |
825 FROM record_owners,domains,records |
1209 WHERE record_owners.user_id = ".$db->quote($_SESSION["userid"])." |
826 WHERE record_owners.user_id = " . $db->quote($_SESSION["userid"]) . " |
1210 AND record_owners.record_id = records.id |
827 AND record_owners.record_id = records.id |
1211 AND records.domain_id = ".$db->quote($id)." |
828 AND records.domain_id = " . $db->quote($id) . " |
1212 GROUP bY record_owners.record_id"); |
829 GROUP BY record_owners.record_id"); |
1213 |
830 |
1214 $ret = array(); |
831 $ret = array(); |
1215 if($result->numRows() == 0) |
832 if($result->numRows() == 0) { |
1216 { |
833 return -1; |
|
834 } else { |
|
835 $ret[] = array(); |
|
836 $retcount = 0; |
|
837 while($r = $result->fetchRow()) |
|
838 { |
|
839 // Call get_record_from_id for each row. |
|
840 $ret[$retcount] = get_record_from_id($r["id"]); |
|
841 $retcount++; |
|
842 } |
|
843 return $ret; |
|
844 } |
|
845 |
|
846 } else { |
|
847 $db->setLimit($rowamount, $rowstart); |
|
848 $result = $db->query("SELECT id FROM records WHERE domain_id=".$db->quote($id)); |
|
849 $ret = array(); |
|
850 if($result->numRows() == 0) |
|
851 { |
|
852 return -1; |
|
853 } |
|
854 else |
|
855 { |
|
856 $ret[] = array(); |
|
857 $retcount = 0; |
|
858 while($r = $result->fetchRow()) |
|
859 { |
|
860 // Call get_record_from_id for each row. |
|
861 $ret[$retcount] = get_record_from_id($r["id"]); |
|
862 $retcount++; |
|
863 } |
|
864 return $ret; |
|
865 } |
|
866 |
|
867 } |
|
868 } |
|
869 else |
|
870 { |
|
871 error(sprintf(ERR_INV_ARG, "get_records_from_domain_id")); |
|
872 } |
|
873 } |
|
874 |
|
875 |
|
876 function get_users_from_domain_id($id) { |
|
877 global $db; |
|
878 $sqlq = "SELECT owner FROM zones WHERE domain_id =" .$db->quote($id); |
|
879 $id_owners = $db->query($sqlq); |
|
880 if ($id_owners->numRows() == 0) { |
1217 return -1; |
881 return -1; |
1218 } |
882 } else { |
1219 else |
883 while ($r = $id_owners->fetchRow()) { |
1220 { |
884 $fullname = $db->queryOne("SELECT fullname FROM users WHERE id=".$r['owner']); |
1221 $ret[] = array(); |
885 $owners[] = array( |
1222 $retcount = 0; |
886 "id" => $r['owner'], |
1223 while($r = $result->fetchRow()) |
887 "fullname" => $fullname |
1224 { |
888 ); |
1225 // Call get_record_from_id for each row. |
889 } |
1226 $ret[$retcount] = get_record_from_id($r["id"]); |
890 } |
1227 $retcount++; |
891 return $owners; |
1228 } |
892 } |
1229 return $ret; |
893 |
1230 } |
894 |
1231 |
895 function search_zone_and_record($holy_grail,$perm) { |
1232 } else { |
896 |
1233 $db->setLimit($rowamount, $rowstart); |
897 global $db; |
1234 $result = $db->query("SELECT id FROM records WHERE domain_id=".$db->quote($id)); |
898 |
1235 $ret = array(); |
899 $holy_grail = trim($holy_grail); |
1236 if($result->numRows() == 0) |
900 |
1237 { |
901 if (verify_permission(zone_content_view_others)) { $perm_view = "all" ; } |
1238 return -1; |
902 elseif (verify_permission(zone_content_view_own)) { $perm_view = "own" ; } |
1239 } |
903 else { $perm_view = "none" ; } |
1240 else |
904 |
1241 { |
905 if (verify_permission(zone_content_edit_others)) { $perm_content_edit = "all" ; } |
1242 $ret[] = array(); |
906 elseif (verify_permission(zone_content_edit_own)) { $perm_content_edit = "own" ; } |
1243 $retcount = 0; |
907 else { $perm_content_edit = "none" ; } |
1244 while($r = $result->fetchRow()) |
908 |
1245 { |
909 // Search for matching domains |
1246 // Call get_record_from_id for each row. |
910 |
1247 $ret[$retcount] = get_record_from_id($r["id"]); |
911 if ($perm == "own") { |
1248 $retcount++; |
912 $sql_add_from = ", zones "; |
1249 } |
913 $sql_add_where = " AND zones.domain_id = domains.id AND zones.owner = " . $db->quote($userid); |
1250 return $ret; |
914 } |
1251 } |
915 |
1252 |
916 $query = "SELECT |
1253 } |
917 domains.id AS zid, |
1254 } |
918 domains.name AS name, |
1255 else |
919 domains.type AS type, |
1256 { |
920 domains.master AS master |
1257 error(sprintf(ERR_INV_ARG, "get_records_from_domain_id")); |
921 FROM domains" . $sql_add_from . " |
1258 } |
922 WHERE domains.name LIKE " . $db->quote($holy_grail) |
1259 } |
923 . $sql_add_where ; |
1260 |
924 |
1261 |
925 $response = $db->query($query); |
1262 function get_users_from_domain_id($id) |
926 if (PEAR::isError($response)) { error($response->getMessage()); return false; } |
1263 { |
927 |
1264 global $db; |
928 while ($r = $response->fetchRow()) { |
1265 $result = $db->queryCol("SELECT owner FROM zones WHERE domain_id=".$db->quote($id)); |
929 $return_zones[] = array( |
1266 $ret = array(); |
930 "zid" => $r['zid'], |
1267 foreach($result as $uid) |
931 "name" => $r['name'], |
1268 { |
932 "type" => $r['type'], |
1269 $fullname = $db->queryOne("SELECT fullname FROM users WHERE id=".$db->quote($uid)); |
933 "master" => $r['master']); |
1270 $ret[] = array( |
934 } |
1271 "id" => $uid, |
935 |
1272 "fullname" => $fullname |
936 // Search for matching records |
1273 ); |
937 |
1274 } |
938 if ($perm == "own") { |
1275 return $ret; |
939 $sql_add_from = ", zones "; |
1276 } |
940 $sql_add_where = " AND zones.domain_id = record.id AND zones.owner = " . $db->quote($userid); |
1277 |
941 } |
1278 function search_record($question) |
942 |
1279 { |
943 $query = "SELECT |
1280 global $db; |
944 records.id AS rid, |
1281 $question = trim($question); |
945 records.name AS name, |
1282 |
946 records.type AS type, |
1283 if (is_valid_search($question)) |
947 records.content AS content, |
1284 { |
948 records.ttl AS ttl, |
1285 $sqlq = "SELECT * |
949 records.prio AS prio, |
1286 FROM records |
950 records.domain_id AS zid |
1287 WHERE content LIKE ".$db->quote($question)." |
951 FROM records" . $sql_add_from . " |
1288 OR name LIKE ".$db->quote($question)." |
952 WHERE (records.name LIKE " . $db->quote($holy_grail) . " OR records.content LIKE " . $db->quote($holy_grail) . ")" |
1289 ORDER BY type DESC"; |
953 . $sql_add_where ; |
1290 $result = $db->query($sqlq); |
954 |
1291 $ret_r = array(); |
955 $response = $db->query($query); |
1292 while ($r = $result->fetchRow()) |
956 if (PEAR::isError($response)) { error($response->getMessage()); return false; } |
1293 { |
957 |
1294 if(xs($r['domain_id'])) |
958 while ($r = $response->fetchRow()) { |
1295 { |
959 $return_records[] = array( |
1296 $ret_r[] = array( |
960 "rid" => $r['rid'], |
1297 'id' => $r['id'], |
961 "name" => $r['name'], |
1298 'domain_id' => $r['domain_id'], |
962 "type" => $r['type'], |
1299 'name' => $r['name'], |
963 "content" => $r['content'], |
1300 'type' => $r['type'], |
964 "ttl" => $r['ttl'], |
1301 'content' => $r['content'], |
965 "zid" => $r['zid'], |
1302 'ttl' => $r['ttl'], |
966 "prio" => $r['prio']); |
1303 'prio' => $r['prio'], |
967 } |
1304 'change_date' => $r['change_date'] |
968 return array('zones' => $return_zones, 'records' => $return_records); |
1305 ); |
969 } |
1306 } |
970 |
1307 } |
971 function get_domain_type($id) { |
1308 |
972 global $db; |
1309 $sqlq = "SELECT domains.id, domains.name, count(records.id) AS numrec, zones.owner, records.domain_id |
973 if (is_numeric($id)) { |
1310 FROM domains LEFT JOIN records ON domains.id = records.domain_id, zones |
|
1311 WHERE zones.domain_id = domains.id |
|
1312 AND domains.name LIKE ".$db->quote($question)." |
|
1313 GROUP BY domains.id, domains.name, zones.owner, records.domain_id"; |
|
1314 $result = $db->query($sqlq); |
|
1315 $ret_d = array(); |
|
1316 while ($r = $result->fetchRow()) |
|
1317 { |
|
1318 if(xs($r['id'])) |
|
1319 { |
|
1320 $ret_d[] = array( |
|
1321 'id' => $r['id'], |
|
1322 'name' => $r['name'], |
|
1323 'numrec' => $r['numrec'], |
|
1324 'owner' => $r['owner'] |
|
1325 ); |
|
1326 } |
|
1327 } |
|
1328 return array('domains' => $ret_d, 'records' => $ret_r); |
|
1329 } |
|
1330 else |
|
1331 { |
|
1332 error(sprintf(ERR_INV_ARGC, "search_record", "Invalid searchstring: $question")); |
|
1333 } |
|
1334 |
|
1335 } |
|
1336 |
|
1337 function get_domain_type($id) |
|
1338 { |
|
1339 global $db; |
|
1340 if (is_numeric($id)) |
|
1341 { |
|
1342 $type = $db->queryOne("SELECT type FROM domains WHERE id = ".$db->quote($id)); |
974 $type = $db->queryOne("SELECT type FROM domains WHERE id = ".$db->quote($id)); |
1343 if($type == "") |
975 if ($type == "") { |
1344 { |
|
1345 $type = "NATIVE"; |
976 $type = "NATIVE"; |
1346 } |
977 } |
1347 return $type; |
978 return $type; |
1348 } |
979 } else { |
1349 else |
|
1350 { |
|
1351 error(sprintf(ERR_INV_ARG, "get_record_from_id", "no or no valid zoneid given")); |
980 error(sprintf(ERR_INV_ARG, "get_record_from_id", "no or no valid zoneid given")); |
1352 } |
981 } |
1353 } |
982 } |
1354 |
983 |
1355 function get_domain_slave_master($id) |
984 function get_domain_slave_master($id){ |
1356 { |
985 global $db; |
1357 global $db; |
986 if (is_numeric($id)) { |
1358 if (is_numeric($id)) |
|
1359 { |
|
1360 $slave_master = $db->queryOne("SELECT master FROM domains WHERE type = 'SLAVE' and id = ".$db->quote($id)); |
987 $slave_master = $db->queryOne("SELECT master FROM domains WHERE type = 'SLAVE' and id = ".$db->quote($id)); |
1361 return $slave_master; |
988 return $slave_master; |
1362 } |
989 } else { |
1363 else |
|
1364 { |
|
1365 error(sprintf(ERR_INV_ARG, "get_domain_slave_master", "no or no valid zoneid given")); |
990 error(sprintf(ERR_INV_ARG, "get_domain_slave_master", "no or no valid zoneid given")); |
1366 } |
991 } |
1367 } |
992 } |
1368 |
993 |
1369 function change_domain_type($type, $id) |
994 function change_zone_type($type, $id) |
1370 { |
995 { |
1371 global $db; |
996 global $db; |
1372 $add = ''; |
997 $add = ''; |
1373 if (is_numeric($id)) |
998 if (is_numeric($id)) |
1374 { |
999 { |
1375 // It is not really neccesary to clear the master field if a |
1000 // It is not really neccesary to clear the field that contains the IP address |
1376 // zone is not of the type "slave" as powerdns will ignore that |
1001 // of the master if the type changes from slave to something else. PowerDNS will |
1377 // fiedl, but it is cleaner anyway. |
1002 // ignore the field if the type isn't something else then slave. But then again, |
1378 if ($type != "SLAVE") |
1003 // it's much clearer this way. |
1379 { |
1004 if ($type != "SLAVE") { |
1380 $add = ", master=''"; |
1005 $add = ", master=''"; |
1381 } |
1006 } |
1382 $result = $db->query("UPDATE domains SET type = " .$db->quote($type). $add." WHERE id = ".$db->quote($id)); |
1007 $result = $db->query("UPDATE domains SET type = " . $db->quote($type) . $add . " WHERE id = ".$db->quote($id)); |
1383 } |
1008 } else { |
1384 else |
|
1385 { |
|
1386 error(sprintf(ERR_INV_ARG, "change_domain_type", "no or no valid zoneid given")); |
1009 error(sprintf(ERR_INV_ARG, "change_domain_type", "no or no valid zoneid given")); |
1387 } |
1010 } |
1388 } |
1011 } |
1389 |
1012 |
1390 function change_domain_slave_master($id, $slave_master) |
1013 function change_zone_slave_master($zone_id, $ip_slave_master) { |
1391 { |
1014 global $db; |
1392 global $db; |
1015 if (is_numeric($zone_id)) { |
1393 if (is_numeric($id)) |
1016 if (is_valid_ip($ip_slave_master) || is_valid_ip6($ip_slave_master)) { |
1394 { |
1017 $result = $db->query("UPDATE domains SET master = " .$db->quote($ip_slave_master). " WHERE id = ".$db->quote($zone_id)); |
1395 if (is_valid_ip($slave_master) || is_valid_ip6($slave_master)) |
1018 } else { |
1396 { |
1019 error(sprintf(ERR_INV_ARGC, "change_domain_ip_slave_master", "This is not a valid IPv4 or IPv6 address: $ip_slave_master")); |
1397 $result = $db->query("UPDATE domains SET master = " .$db->quote($slave_master). " WHERE id = ".$db->quote($id)); |
1020 } |
1398 } |
1021 } else { |
1399 else |
|
1400 { |
|
1401 error(sprintf(ERR_INV_ARGC, "change_domain_slave_master", "This is not a valid IPv4 or IPv6 address: $slave_master")); |
|
1402 } |
|
1403 } |
|
1404 else |
|
1405 { |
|
1406 error(sprintf(ERR_INV_ARG, "change_domain_type", "no or no valid zoneid given")); |
1022 error(sprintf(ERR_INV_ARG, "change_domain_type", "no or no valid zoneid given")); |
1407 } |
1023 } |
1408 } |
1024 } |
1409 |
1025 |
1410 |
1026 |
1411 function validate_account($account) |
1027 function validate_account($account) { |
1412 { |
1028 if(preg_match("/^[A-Z0-9._-]+$/i",$account)) { |
1413 |
|
1414 if(preg_match("/^[A-Z0-9._-]+$/i",$account)) |
|
1415 { |
|
1416 return true; |
1029 return true; |
1417 } |
1030 } else { |
1418 else |
|
1419 { |
|
1420 return false; |
1031 return false; |
1421 } |
1032 } |
1422 } |
1033 } |
|
1034 |
|
1035 |
1423 ?> |
1036 ?> |