-1) { $querystr = "SELECT * FROM \"$table\" WHERE type = $type;"; } else { $querystr = "SELECT * FROM \"$table\";"; } $results = $db->query($querystr); // Return early without creating a file if the // requested table cannot be accessed if(is_null($results)) return; $content = array(); while ($row = $results->fetchArray(SQLITE3_ASSOC)) { array_push($content, $row); } $archive[$name] = json_encode($content); } /** * Restore the contents of a table from an uploaded archive * * @param $file object The file in the archive to restore the table from * @param $table string The table to import * @param $flush boolean Whether to flush the table before importing the archived data * @return integer Number of restored rows */ function archive_restore_table($file, $table, $flush=false) { global $db, $flushed_tables; $json_string = file_get_contents($file); // Return early if we cannot extract the JSON string if(is_null($json_string)) return 0; $contents = json_decode($json_string, true); // Return early if we cannot decode the JSON string if(is_null($contents)) return 0; // Flush table if requested, only flush each table once if($flush && !in_array($table, $flushed_tables)) { $db->exec("DELETE FROM \"".$table."\""); array_push($flushed_tables, $table); } // Prepare fields depending on the table we restore to if($table === "adlist") { $sql = "INSERT OR IGNORE INTO adlist"; $sql .= " (id,address,enabled,date_added,comment)"; $sql .= " VALUES (:id,:address,:enabled,:date_added,:comment);"; } elseif($table === "domain_audit") { $sql = "INSERT OR IGNORE INTO domain_audit"; $sql .= " (id,domain,date_added)"; $sql .= " VALUES (:id,:domain,:date_added);"; } elseif($table === "domainlist") { $sql = "INSERT OR IGNORE INTO domainlist"; $sql .= " (id,domain,enabled,date_added,comment,type)"; $sql .= " VALUES (:id,:domain,:enabled,:date_added,:comment,:type);"; } elseif($table === "group") { $sql = "INSERT OR IGNORE INTO \"group\""; $sql .= " (id,name,date_added,description)"; $sql .= " VALUES (:id,:name,:date_added,:description);"; } elseif($table === "client") { $sql = "INSERT OR IGNORE INTO client"; $sql .= " (id,ip,date_added,comment)"; $sql .= " VALUES (:id,:ip,:date_added,:comment);"; } elseif($table === "domainlist_by_group") { $sql = "INSERT OR IGNORE INTO domainlist_by_group"; $sql .= " (domainlist_id,group_id)"; $sql .= " VALUES (:domainlist_id,:group_id);"; } elseif($table === "client_by_group") { $sql = "INSERT OR IGNORE INTO client_by_group"; $sql .= " (client_id,group_id)"; $sql .= " VALUES (:client_id,:group_id);"; } elseif($table === "adlist_by_group") { $sql = "INSERT OR IGNORE INTO adlist_by_group"; $sql .= " (adlist_id,group_id)"; $sql .= " VALUES (:adlist_id,:group_id);"; } else { if($table === "whitelist") $type = 0; elseif($table === "blacklist") $type = 1; elseif($table === "regex_whitelist") $type = 2; elseif($table === "regex_blacklist") $type = 3; $sql = "INSERT OR IGNORE INTO domainlist"; $sql .= " (id,domain,enabled,date_added,comment,type)"; $sql .= " VALUES (:id,:domain,:enabled,:date_added,:comment,$type);"; $field = "domain"; } // Prepare SQLite statememt $stmt = $db->prepare($sql); // Return early if we fail to prepare the SQLite statement if(!$stmt) { echo "Failed to prepare statement for ".$table." table."; echo $sql; return 0; } // Loop over rows and inject the entries into the database $num = 0; foreach($contents as $row) { // Limit max length for a domain entry to 253 chars if(isset($field) && strlen($row[$field]) > 253) continue; // Bind properties from JSON data // Note that only defined above are actually used // so even maliciously modified Teleporter files // cannot be dangerous in any way foreach($row as $key => $value) { $type = gettype($value); $sqltype=NULL; switch($type) { case "integer": $sqltype = SQLITE3_INTEGER; break; case "string": $sqltype = SQLITE3_TEXT; break; case "NULL": $sqltype = SQLITE3_NULL; break; default: $sqltype = "UNK"; } $stmt->bindValue(":".$key, htmlentities($value), $sqltype); } if($stmt->execute() && $stmt->reset() && $stmt->clear()) $num++; else { $stmt->close(); return $num; } } // Close database connection and return number or processed rows $stmt->close(); return $num; } /** * Create table rows from an uploaded archive file * * @param $file object The file in the archive to import * @param $table string The target table * @param $flush boolean Whether to flush the table before importing the archived data * @param $wildcardstyle boolean Whether to format the input domains in legacy wildcard notation * @return integer Number of processed rows from the imported file */ function archive_insert_into_table($file, $table, $flush=false, $wildcardstyle=false) { global $db; $domains = array_filter(explode("\n",file_get_contents($file))); // Return early if we cannot extract the lines in the file if(is_null($domains)) return 0; // Generate comment $prefix = "phar:///tmp/"; if (substr($file, 0, strlen($prefix)) == $prefix) { $file = substr($file, strlen($prefix)); } $comment = "Imported from ".$file; // Determine table and type to import to $type = null; if($table === "whitelist") { $table = "domainlist"; $type = ListType::whitelist; } else if($table === "blacklist") { $table = "domainlist"; $type = ListType::blacklist; } else if($table === "regex_blacklist") { $table = "domainlist"; $type = ListType::regex_blacklist; } else if($table === "domain_audit") { $table = "domain_audit"; $type = -1; // -1 -> not used inside add_to_table() } else if($table === "adlist") { $table = "adlist"; $type = -1; // -1 -> not used inside add_to_table() } // Flush table if requested if($flush) { flush_table($table, $type); } // Add domains to requested table return add_to_table($db, $table, $domains, $comment, $wildcardstyle, true, $type); } /** * Flush table if requested. This subroutine flushes each table only once * * @param $table string The target table * @param $type integer Type of item to flush in table (applies only to domainlist table) */ function flush_table($table, $type=null) { global $db, $flushed_tables; if(!in_array($table, $flushed_tables)) { if($type !== null) { $sql = "DELETE FROM \"".$table."\" WHERE type = ".$type; array_push($flushed_tables, $table.$type); } else { $sql = "DELETE FROM \"".$table."\""; array_push($flushed_tables, $table); } $db->exec($sql); } } function archive_add_directory($path,$subdir="") { if($dir = opendir($path)) { while(false !== ($entry = readdir($dir))) { if($entry !== "." && $entry !== "..") { archive_add_file($path,$entry,$subdir); } } closedir($dir); } } function limit_length(&$item, $key) { // limit max length for a domain entry to 253 chars // return only a part of the string if it is longer $item = substr($item, 0, 253); } function process_file($contents) { $domains = array_filter(explode("\n",$contents)); // Walk array and apply a max string length // function to every member of the array of domains array_walk($domains, "limit_length"); return $domains; } if(isset($_POST["action"])) { if($_FILES["zip_file"]["name"] && $_POST["action"] == "in") { $filename = $_FILES["zip_file"]["name"]; $source = $_FILES["zip_file"]["tmp_name"]; $type = mime_content_type($source); $name = explode(".", $filename); $accepted_types = array('application/gzip', 'application/tar', 'application/x-compressed', 'application/x-gzip'); $okay = false; foreach($accepted_types as $mime_type) { if($mime_type == $type) { $okay = true; break; } } $continue = strtolower($name[1]) == 'tar' && strtolower($name[2]) == 'gz' ? true : false; if(!$continue || !$okay) { die("The file you are trying to upload is not a .tar.gz file (filename: ".htmlentities($filename).", type: ".htmlentities($type)."). Please try again."); } $fullfilename = sys_get_temp_dir()."/".$filename; if(!move_uploaded_file($source, $fullfilename)) { die("Failed moving ".htmlentities($source)." to ".htmlentities($fullfilename)); } $archive = new PharData($fullfilename); $importedsomething = false; $flushtables = isset($_POST["flushtables"]); foreach(new RecursiveIteratorIterator($archive) as $file) { if(isset($_POST["blacklist"]) && $file->getFilename() === "blacklist.txt") { $num = archive_insert_into_table($file, "blacklist", $flushtables); echo "Processed blacklist (exact) (".$num." entries)
\n"; $importedsomething = true; } if(isset($_POST["whitelist"]) && $file->getFilename() === "whitelist.txt") { $num = archive_insert_into_table($file, "whitelist", $flushtables); echo "Processed whitelist (exact) (".$num." entries)
\n"; $importedsomething = true; } if(isset($_POST["regexlist"]) && $file->getFilename() === "regex.list") { $num = archive_insert_into_table($file, "regex_blacklist", $flushtables); echo "Processed blacklist (regex) (".$num." entries)
\n"; $importedsomething = true; } // Also try to import legacy wildcard list if found if(isset($_POST["regexlist"]) && $file->getFilename() === "wildcardblocking.txt") { $num = archive_insert_into_table($file, "regex_blacklist", $flushtables, true); echo "Processed blacklist (regex, wildcard style) (".$num." entries)
\n"; $importedsomething = true; } if(isset($_POST["auditlog"]) && $file->getFilename() === "auditlog.list") { $num = archive_insert_into_table($file, "domain_audit", $flushtables); echo "Processed audit log (".$num." entries)
\n"; $importedsomething = true; } if(isset($_POST["adlist"]) && $file->getFilename() === "adlists.list") { $num = archive_insert_into_table($file, "adlist", $flushtables); echo "Processed adlists (".$num." entries)
\n"; $importedsomething = true; } if(isset($_POST["blacklist"]) && $file->getFilename() === "blacklist.exact.json") { $num = archive_restore_table($file, "blacklist", $flushtables); echo "Processed blacklist (exact) (".$num." entries)
\n"; $importedsomething = true; } if(isset($_POST["regexlist"]) && $file->getFilename() === "blacklist.regex.json") { $num = archive_restore_table($file, "regex_blacklist", $flushtables); echo "Processed blacklist (regex) (".$num." entries)
\n"; $importedsomething = true; } if(isset($_POST["whitelist"]) && $file->getFilename() === "whitelist.exact.json") { $num = archive_restore_table($file, "whitelist", $flushtables); echo "Processed whitelist (exact) (".$num." entries)
\n"; $importedsomething = true; } if(isset($_POST["regex_whitelist"]) && $file->getFilename() === "whitelist.regex.json") { $num = archive_restore_table($file, "regex_whitelist", $flushtables); echo "Processed whitelist (regex) (".$num." entries)
\n"; $importedsomething = true; } if(isset($_POST["adlist"]) && $file->getFilename() === "adlist.json") { $num = archive_restore_table($file, "adlist", $flushtables); echo "Processed adlist (".$num." entries)
\n"; $importedsomething = true; } if(isset($_POST["auditlog"]) && $file->getFilename() === "domain_audit.json") { $num = archive_restore_table($file, "domain_audit", $flushtables); echo "Processed domain_audit (".$num." entries)
\n"; $importedsomething = true; } if(isset($_POST["group"]) && $file->getFilename() === "group.json") { $num = archive_restore_table($file, "group", $flushtables); echo "Processed group (".$num." entries)
\n"; $importedsomething = true; } if(isset($_POST["client"]) && $file->getFilename() === "client.json") { $num = archive_restore_table($file, "client", $flushtables); echo "Processed client (".$num." entries)
\n"; $importedsomething = true; } if(isset($_POST["client"]) && $file->getFilename() === "client_by_group.json") { $num = archive_restore_table($file, "client_by_group", $flushtables); echo "Processed client group assignments (".$num." entries)
\n"; $importedsomething = true; } if((isset($_POST["whitelist"]) || isset($_POST["regex_whitelist"]) || isset($_POST["blacklist"]) || isset($_POST["regex_blacklist"])) && $file->getFilename() === "domainlist_by_group.json") { $num = archive_restore_table($file, "domainlist_by_group", $flushtables); echo "Processed black-/whitelist group assignments (".$num." entries)
\n"; $importedsomething = true; } if(isset($_POST["adlist"]) && $file->getFilename() === "adlist_by_group.json") { $num = archive_restore_table($file, "adlist_by_group", $flushtables); echo "Processed adlist group assignments (".$num." entries)
\n"; $importedsomething = true; } if(isset($_POST["staticdhcpleases"]) && $file->getFilename() === "04-pihole-static-dhcp.conf") { if($flushtables) { $local_file = @fopen("/etc/dnsmasq.d/04-pihole-static-dhcp.conf", "r+"); if ($local_file !== false) { ftruncate($local_file, 0); fclose($local_file); } } $num = 0; $staticdhcpleases = process_file(file_get_contents($file)); foreach($staticdhcpleases as $lease) { list($mac,$ip,$hostname) = explode(",",$lease); $mac = formatMAC($mac); if(addStaticDHCPLease($mac,$ip,$hostname)) $num++; } readStaticLeasesFile(); echo "Processed static DHCP leases (".$num." entries)
\n"; if($num > 0) { $importedsomething = true; } } if(isset($_POST["localdnsrecords"]) && $file->getFilename() === "custom.list") { if($flushtables) { // Defined in func.php included via auth.php deleteAllCustomDNSEntries(); } $num = 0; $localdnsrecords = process_file(file_get_contents($file)); foreach($localdnsrecords as $record) { list($ip,$domain) = explode(" ",$record); if(addCustomDNSEntry($ip, $domain, false)) $num++; } echo "Processed local DNS records (".$num." entries)
\n"; if($num > 0) { $importedsomething = true; } } } if($importedsomething) { pihole_execute("restartdns reload"); } unlink($fullfilename); echo "OK"; } else { die("No file transmitted or parameter error."); } } else { $hostname = gethostname() ? gethostname()."-" : ""; $tarname = "pi-hole-".$hostname."teleporter_".date("Y-m-d_H-i-s").".tar"; $filename = $tarname.".gz"; $archive_file_name = sys_get_temp_dir() ."/". $tarname; $archive = new PharData($archive_file_name); if ($archive->isWritable() !== TRUE) { exit("cannot open/create ".htmlentities($archive_file_name)."
\nPHP user: ".exec('whoami')."\n"); } archive_add_table("whitelist.exact.json", "domainlist", ListType::whitelist); archive_add_table("whitelist.regex.json", "domainlist", ListType::regex_whitelist); archive_add_table("blacklist.exact.json", "domainlist", ListType::blacklist); archive_add_table("blacklist.regex.json", "domainlist", ListType::regex_blacklist); archive_add_table("adlist.json", "adlist"); archive_add_table("domain_audit.json", "domain_audit"); archive_add_table("group.json", "group"); archive_add_table("client.json", "client"); // Group linking tables archive_add_table("domainlist_by_group.json", "domainlist_by_group"); archive_add_table("adlist_by_group.json", "adlist_by_group"); archive_add_table("client_by_group.json", "client_by_group"); archive_add_file("/etc/pihole/","setupVars.conf"); archive_add_file("/etc/pihole/","dhcp.leases"); archive_add_file("/etc/pihole/","custom.list"); archive_add_file("/etc/pihole/","pihole-FTL.conf"); archive_add_file("/etc/","hosts","etc/"); archive_add_directory("/etc/dnsmasq.d/","dnsmasq.d/"); $archive->compress(Phar::GZ); // Creates a gziped copy unlink($archive_file_name); // Unlink original tar file as it is not needed anymore $archive_file_name .= ".gz"; // Append ".gz" extension to ".tar" header("Content-type: application/gzip"); header('Content-Transfer-Encoding: binary'); header("Content-Disposition: attachment; filename=".$filename); header("Content-length: " . filesize($archive_file_name)); header("Pragma: no-cache"); header("Expires: 0"); if(ob_get_length() > 0) ob_end_clean(); readfile($archive_file_name); ignore_user_abort(true); unlink($archive_file_name); exit; } ?>