File indexing completed on 2024-12-22 05:36:19

0001 <?php
0002 
0003 class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCache
0004 {
0005 
0006     /**
0007      * @param HTMLPurifier_Definition $def
0008      * @param HTMLPurifier_Config $config
0009      * @return int|bool
0010      */
0011     public function add($def, $config)
0012     {
0013         if (!$this->checkDefType($def)) {
0014             return;
0015         }
0016         $file = $this->generateFilePath($config);
0017         if (file_exists($file)) {
0018             return false;
0019         }
0020         if (!$this->_prepareDir($config)) {
0021             return false;
0022         }
0023         return $this->_write($file, serialize($def), $config);
0024     }
0025 
0026     /**
0027      * @param HTMLPurifier_Definition $def
0028      * @param HTMLPurifier_Config $config
0029      * @return int|bool
0030      */
0031     public function set($def, $config)
0032     {
0033         if (!$this->checkDefType($def)) {
0034             return;
0035         }
0036         $file = $this->generateFilePath($config);
0037         if (!$this->_prepareDir($config)) {
0038             return false;
0039         }
0040         return $this->_write($file, serialize($def), $config);
0041     }
0042 
0043     /**
0044      * @param HTMLPurifier_Definition $def
0045      * @param HTMLPurifier_Config $config
0046      * @return int|bool
0047      */
0048     public function replace($def, $config)
0049     {
0050         if (!$this->checkDefType($def)) {
0051             return;
0052         }
0053         $file = $this->generateFilePath($config);
0054         if (!file_exists($file)) {
0055             return false;
0056         }
0057         if (!$this->_prepareDir($config)) {
0058             return false;
0059         }
0060         return $this->_write($file, serialize($def), $config);
0061     }
0062 
0063     /**
0064      * @param HTMLPurifier_Config $config
0065      * @return bool|HTMLPurifier_Config
0066      */
0067     public function get($config)
0068     {
0069         $file = $this->generateFilePath($config);
0070         if (!file_exists($file)) {
0071             return false;
0072         }
0073         return unserialize(file_get_contents($file));
0074     }
0075 
0076     /**
0077      * @param HTMLPurifier_Config $config
0078      * @return bool
0079      */
0080     public function remove($config)
0081     {
0082         $file = $this->generateFilePath($config);
0083         if (!file_exists($file)) {
0084             return false;
0085         }
0086         return unlink($file);
0087     }
0088 
0089     /**
0090      * @param HTMLPurifier_Config $config
0091      * @return bool
0092      */
0093     public function flush($config)
0094     {
0095         if (!$this->_prepareDir($config)) {
0096             return false;
0097         }
0098         $dir = $this->generateDirectoryPath($config);
0099         $dh = opendir($dir);
0100         // Apparently, on some versions of PHP, readdir will return
0101         // an empty string if you pass an invalid argument to readdir.
0102         // So you need this test.  See #49.
0103         if (false === $dh) {
0104             return false;
0105         }
0106         while (false !== ($filename = readdir($dh))) {
0107             if (empty($filename)) {
0108                 continue;
0109             }
0110             if ($filename[0] === '.') {
0111                 continue;
0112             }
0113             unlink($dir . '/' . $filename);
0114         }
0115         closedir($dh);
0116         return true;
0117     }
0118 
0119     /**
0120      * @param HTMLPurifier_Config $config
0121      * @return bool
0122      */
0123     public function cleanup($config)
0124     {
0125         if (!$this->_prepareDir($config)) {
0126             return false;
0127         }
0128         $dir = $this->generateDirectoryPath($config);
0129         $dh = opendir($dir);
0130         // See #49 (and above).
0131         if (false === $dh) {
0132             return false;
0133         }
0134         while (false !== ($filename = readdir($dh))) {
0135             if (empty($filename)) {
0136                 continue;
0137             }
0138             if ($filename[0] === '.') {
0139                 continue;
0140             }
0141             $key = substr($filename, 0, strlen($filename) - 4);
0142             if ($this->isOld($key, $config)) {
0143                 unlink($dir . '/' . $filename);
0144             }
0145         }
0146         closedir($dh);
0147         return true;
0148     }
0149 
0150     /**
0151      * Generates the file path to the serial file corresponding to
0152      * the configuration and definition name
0153      * @param HTMLPurifier_Config $config
0154      * @return string
0155      * @todo Make protected
0156      */
0157     public function generateFilePath($config)
0158     {
0159         $key = $this->generateKey($config);
0160         return $this->generateDirectoryPath($config) . '/' . $key . '.ser';
0161     }
0162 
0163     /**
0164      * Generates the path to the directory contain this cache's serial files
0165      * @param HTMLPurifier_Config $config
0166      * @return string
0167      * @note No trailing slash
0168      * @todo Make protected
0169      */
0170     public function generateDirectoryPath($config)
0171     {
0172         $base = $this->generateBaseDirectoryPath($config);
0173         return $base . '/' . $this->type;
0174     }
0175 
0176     /**
0177      * Generates path to base directory that contains all definition type
0178      * serials
0179      * @param HTMLPurifier_Config $config
0180      * @return mixed|string
0181      * @todo Make protected
0182      */
0183     public function generateBaseDirectoryPath($config)
0184     {
0185         $base = $config->get('Cache.SerializerPath');
0186         $base = is_null($base) ? HTMLPURIFIER_PREFIX . '/HTMLPurifier/DefinitionCache/Serializer' : $base;
0187         return $base;
0188     }
0189 
0190     /**
0191      * Convenience wrapper function for file_put_contents
0192      * @param string $file File name to write to
0193      * @param string $data Data to write into file
0194      * @param HTMLPurifier_Config $config
0195      * @return int|bool Number of bytes written if success, or false if failure.
0196      */
0197     private function _write($file, $data, $config)
0198     {
0199         $result = file_put_contents($file, $data);
0200         if ($result !== false) {
0201             // set permissions of the new file (no execute)
0202             $chmod = $config->get('Cache.SerializerPermissions');
0203             if ($chmod !== null) {
0204                 chmod($file, $chmod & 0666);
0205             }
0206         }
0207         return $result;
0208     }
0209 
0210     /**
0211      * Prepares the directory that this type stores the serials in
0212      * @param HTMLPurifier_Config $config
0213      * @return bool True if successful
0214      */
0215     private function _prepareDir($config)
0216     {
0217         $directory = $this->generateDirectoryPath($config);
0218         $chmod = $config->get('Cache.SerializerPermissions');
0219         if ($chmod === null) {
0220             // TODO: This races
0221             if (is_dir($directory)) return true;
0222             return mkdir($directory);
0223         }
0224         if (!is_dir($directory)) {
0225             $base = $this->generateBaseDirectoryPath($config);
0226             if (!is_dir($base)) {
0227                 trigger_error(
0228                     'Base directory ' . $base . ' does not exist,
0229                     please create or change using %Cache.SerializerPath',
0230                     E_USER_WARNING
0231                 );
0232                 return false;
0233             } elseif (!$this->_testPermissions($base, $chmod)) {
0234                 return false;
0235             }
0236             if (!mkdir($directory, $chmod)) {
0237                 trigger_error(
0238                     'Could not create directory ' . $directory . '',
0239                     E_USER_WARNING
0240                 );
0241                 return false;
0242             }
0243             if (!$this->_testPermissions($directory, $chmod)) {
0244                 return false;
0245             }
0246         } elseif (!$this->_testPermissions($directory, $chmod)) {
0247             return false;
0248         }
0249         return true;
0250     }
0251 
0252     /**
0253      * Tests permissions on a directory and throws out friendly
0254      * error messages and attempts to chmod it itself if possible
0255      * @param string $dir Directory path
0256      * @param int $chmod Permissions
0257      * @return bool True if directory is writable
0258      */
0259     private function _testPermissions($dir, $chmod)
0260     {
0261         // early abort, if it is writable, everything is hunky-dory
0262         if (is_writable($dir)) {
0263             return true;
0264         }
0265         if (!is_dir($dir)) {
0266             // generally, you'll want to handle this beforehand
0267             // so a more specific error message can be given
0268             trigger_error(
0269                 'Directory ' . $dir . ' does not exist',
0270                 E_USER_WARNING
0271             );
0272             return false;
0273         }
0274         if (function_exists('posix_getuid') && $chmod !== null) {
0275             // POSIX system, we can give more specific advice
0276             if (fileowner($dir) === posix_getuid()) {
0277                 // we can chmod it ourselves
0278                 $chmod = $chmod | 0700;
0279                 if (chmod($dir, $chmod)) {
0280                     return true;
0281                 }
0282             } elseif (filegroup($dir) === posix_getgid()) {
0283                 $chmod = $chmod | 0070;
0284             } else {
0285                 // PHP's probably running as nobody, so we'll
0286                 // need to give global permissions
0287                 $chmod = $chmod | 0777;
0288             }
0289             trigger_error(
0290                 'Directory ' . $dir . ' not writable, ' .
0291                 'please chmod to ' . decoct($chmod),
0292                 E_USER_WARNING
0293             );
0294         } else {
0295             // generic error message
0296             trigger_error(
0297                 'Directory ' . $dir . ' not writable, ' .
0298                 'please alter file permissions',
0299                 E_USER_WARNING
0300             );
0301         }
0302         return false;
0303     }
0304 }
0305 
0306 // vim: et sw=4 sts=4