base.php000066600000012562152140277770006215 0ustar00files_manager->get( get_called_class(), func_get_args() ); } /** * @since 2.1.0 * @access public */ public function __construct( $file_name ) { /** * Elementor File Name * * Filters the File name * * @since 2.3.0 * * @param string $file_name * @param object $this The file instance, which inherits Elementor\Core\Files */ $file_name = apply_filters( 'elementor/files/file_name', $file_name, $this ); $this->set_file_name( $file_name ); $this->set_files_dir( static::DEFAULT_FILES_DIR ); $this->set_path(); } /** * @since 2.1.0 * @access public */ public function set_files_dir( $files_dir ) { $this->files_dir = $files_dir; } /** * @since 2.1.0 * @access public */ public function set_file_name( $file_name ) { $this->file_name = $file_name; } /** * @since 2.1.0 * @access public */ public function get_file_name() { return $this->file_name; } /** * @since 2.1.0 * @access public */ public function get_url() { $url = set_url_scheme( self::get_base_uploads_url() . $this->files_dir . $this->file_name ); return add_query_arg( [ 'ver' => $this->get_meta( 'time' ) ], $url ); } /** * @since 2.1.0 * @access public */ public function get_content() { if ( ! $this->content ) { $this->content = $this->parse_content(); } return $this->content; } /** * @since 2.1.0 * @access public */ public function update() { $this->update_file(); $meta = $this->get_meta(); $meta['time'] = time(); $this->update_meta( $meta ); } /** * @since 2.1.0 * @access public */ public function update_file() { $this->content = $this->parse_content(); if ( $this->content ) { $this->write(); } else { $this->delete(); } } /** * @since 2.1.0 * @access public */ public function write() { return file_put_contents( $this->path, $this->content ); } /** * @since 2.1.0 * @access public */ public function delete() { if ( file_exists( $this->path ) ) { unlink( $this->path ); } $this->delete_meta(); } /** * Get meta data. * * Retrieve the CSS file meta data. Returns an array of all the data, or if * custom property is given it will return the property value, or `null` if * the property does not exist. * * @since 2.1.0 * @access public * * @param string $property Optional. Custom meta data property. Default is * null. * * @return array|null An array of all the data, or if custom property is * given it will return the property value, or `null` if * the property does not exist. */ public function get_meta( $property = null ) { $default_meta = $this->get_default_meta(); $meta = array_merge( $default_meta, (array) $this->load_meta() ); if ( $property ) { return isset( $meta[ $property ] ) ? $meta[ $property ] : null; } return $meta; } /** * @since 2.1.0 * @access protected * @abstract */ abstract protected function parse_content(); /** * Load meta. * * Retrieve the file meta data. * * @since 2.1.0 * @access protected */ protected function load_meta() { return get_option( static::META_KEY ); } /** * Update meta. * * Update the file meta data. * * @since 2.1.0 * @access protected * * @param array $meta New meta data. */ protected function update_meta( $meta ) { update_option( static::META_KEY, $meta ); } /** * Delete meta. * * Delete the file meta data. * * @since 2.1.0 * @access protected */ protected function delete_meta() { delete_option( static::META_KEY ); } /** * @since 2.1.0 * @access protected */ protected function get_default_meta() { return [ 'time' => 0, ]; } /** * @since 2.1.0 * @access private * @static */ private static function get_wp_uploads_dir() { global $blog_id; if ( empty( self::$wp_uploads_dir[ $blog_id ] ) ) { self::$wp_uploads_dir[ $blog_id ] = wp_upload_dir( null, false ); } return self::$wp_uploads_dir[ $blog_id ]; } /** * @since 2.1.0 * @access private */ private function set_path() { $dir_path = self::get_base_uploads_dir() . $this->files_dir; if ( ! is_dir( $dir_path ) ) { wp_mkdir_p( $dir_path ); } $this->path = $dir_path . $this->file_name; } } manager.php000066600000005714152140277770006716 0ustar00register_actions(); } public function get( $class, $args ) { $id = $class . '-' . wp_json_encode( $args ); if ( ! isset( $this->files[ $id ] ) ) { // Create an instance from dynamic args length. $reflection_class = new \ReflectionClass( $class ); $this->files[ $id ] = $reflection_class->newInstanceArgs( $args ); } return $this->files[ $id ]; } /** * On post delete. * * Delete post CSS immediately after a post is deleted from the database. * * Fired by `deleted_post` action. * * @since 1.2.0 * @access public * * @param string $post_id Post ID. */ public function on_delete_post( $post_id ) { if ( ! Utils::is_post_support( $post_id ) ) { return; } $css_file = Post_CSS::create( $post_id ); $css_file->delete(); } /** * On export post meta. * * When exporting data using WXR, skip post CSS file meta key. This way the * export won't contain the post CSS file data used by Elementor. * * Fired by `wxr_export_skip_postmeta` filter. * * @since 1.2.0 * @access public * * @param bool $skip Whether to skip the current post meta. * @param string $meta_key Current meta key. * * @return bool Whether to skip the post CSS meta. */ public function on_export_post_meta( $skip, $meta_key ) { if ( Post_CSS::META_KEY === $meta_key ) { $skip = true; } return $skip; } /** * Clear cache. * * Delete all meta containing files data. And delete the actual * files from the upload directory. * * @since 1.2.0 * @access public */ public function clear_cache() { delete_post_meta_by_key( Post_CSS::META_KEY ); delete_option( Global_CSS::META_KEY ); delete_option( Frontend::META_KEY ); // Delete files. $path = Base::get_base_uploads_dir() . Base::DEFAULT_FILES_DIR . '*'; foreach ( glob( $path ) as $file_path ) { unlink( $file_path ); } /** * Elementor clear files. * * Fires after Elementor clears files * * @since 2.1.0 */ do_action( 'elementor/core/files/clear_cache' ); } /** * Register actions. * * Register filters and actions for the files manager. * * @since 1.2.0 * @access private */ private function register_actions() { add_action( 'deleted_post', [ $this, 'on_delete_post' ] ); add_filter( 'wxr_export_skip_postmeta', [ $this, 'on_export_post_meta' ], 10, 2 ); } } assets/svg/svg-handler.php000066600000040775152140277770011625 0ustar00attachment_id = $attachment_id; return $this->attachment_id; } /** * get_attachment_id * @return int */ public function get_attachment_id() { return $this->attachment_id; } /** * get_meta * @return mixed */ protected function get_meta() { return get_post_meta( $this->attachment_id, self::META_KEY, true ); } /** * update_meta * @param $meta */ protected function update_meta( $meta ) { update_post_meta( $this->attachment_id, self::META_KEY, $meta ); } /** * delete_meta */ protected function delete_meta() { delete_post_meta( $this->attachment_id, self::META_KEY ); } /** * delete_meta_cache */ public function delete_meta_cache() { delete_post_meta_by_key( self::META_KEY ); } /** * read_from_file * @return bool|string */ public function read_from_file() { return file_get_contents( get_attached_file( $this->attachment_id ) ); } /** * get_inline_svg * @param $attachment_id * * @return bool|mixed|string */ public static function get_inline_svg( $attachment_id ) { $svg = get_post_meta( $attachment_id, self::META_KEY, true ); if ( ! empty( $svg ) ) { return $svg; } $attachment_file = get_attached_file( $attachment_id ); if ( ! $attachment_file ) { return ''; } $svg = file_get_contents( $attachment_file ); if ( ! empty( $svg ) ) { update_post_meta( $attachment_id, self::META_KEY, $svg ); } return $svg; } public function upload_mimes( $allowed_types ) { if ( $this->is_elementor_media_upload() ) { $allowed_types['svg'] = self::MIME_TYPE; } return $allowed_types; } /** * wp_handle_upload_prefilter * @param $file * * @return mixed */ public function wp_handle_upload_prefilter( $file ) { if ( ! $this->is_elementor_media_upload() || self::MIME_TYPE !== $file['type'] ) { return $file; } $ext = pathinfo( $file['name'], PATHINFO_EXTENSION ); if ( 'svg' !== $ext ) { $file['error'] = sprintf( __( 'The uploaded %s file is not supported. Please upload a valid SVG file', 'elementor' ), $ext ); return $file; } if ( ! self::is_enabled() ) { $file['error'] = __( 'SVG file is not allowed for security reasons', 'elementor' ); return $file; } if ( self::svg_sanitizer_can_run() && ! $this->sanitize_svg( $file['tmp_name'] ) ) { $file['error'] = __( 'Invalid SVG Format, file not uploaded for security reasons', 'elementor' ); } return $file; } /** * is_elementor_media_upload * @return bool */ private function is_elementor_media_upload() { return isset( $_POST['uploadTypeCaller'] ) && 'elementor-editor-upload' === $_POST['uploadTypeCaller']; // phpcs:ignore } /** * wp_check_filetype_and_ext * A workaround for upload validation which relies on a PHP extension (fileinfo) * with inconsistent reporting behaviour. * ref: https://core.trac.wordpress.org/ticket/39550 * ref: https://core.trac.wordpress.org/ticket/40175 * * @param $data * @param $file * @param $filename * @param $mimes * * @return mixed */ public function wp_check_filetype_and_ext( $data, $file, $filename, $mimes ) { if ( ! empty( $data['ext'] ) && ! empty( $data['type'] ) ) { return $data; } $filetype = wp_check_filetype( $filename, $mimes ); if ( 'svg' === $filetype['ext'] ) { $data['ext'] = 'svg'; $data['type'] = self::MIME_TYPE; } return $data; } /** * Check if the contents are gzipped * @see http://www.gzip.org/zlib/rfc-gzip.html#member-format * * @param $contents * @return bool */ private function is_encoded( $contents ) { $needle = "\x1f\x8b\x08"; if ( function_exists( 'mb_strpos' ) ) { return 0 === mb_strpos( $contents, $needle ); } else { return 0 === strpos( $contents, $needle ); } } /** * decode_svg * @param $content * * @return string */ private function decode_svg( $content ) { return gzdecode( $content ); } /** * encode_svg * @param $content * * @return string */ private function encode_svg( $content ) { return gzencode( $content ); } /** * sanitize_svg * @param $filename * * @return bool */ public function sanitize_svg( $filename ) { $original_content = file_get_contents( $filename ); $is_encoded = $this->is_encoded( $original_content ); if ( $is_encoded ) { $decoded = $this->decode_svg( $original_content ); if ( false === $decoded ) { return false; } $original_content = $decoded; } $valid_svg = $this->sanitizer( $original_content ); if ( false === $valid_svg ) { return false; } // If we were gzipped, we need to re-zip if ( $is_encoded ) { $valid_svg = $this->encode_svg( $valid_svg ); } file_put_contents( $filename, $valid_svg ); return true; } /** * is_allowed_tag * @param $element * * @return bool */ private function is_allowed_tag( $element ) { static $allowed_tags = false; if ( false === $allowed_tags ) { $allowed_tags = $this->get_allowed_elements(); } $tag_name = $element->tagName; // phpcs:ignore -- php DomDocument if ( ! in_array( strtolower( $tag_name ), $allowed_tags ) ) { $this->remove_element( $element ); return false; } return true; } private function remove_element( $element ) { $element->parentNode->removeChild( $element ); // phpcs:ignore -- php DomDocument } /** * is_a_attribute * @param $name * @param $check * * @return bool */ private function is_a_attribute( $name, $check ) { return 0 === strpos( $name, $check . '-' ); } /** * is_remote_value * @param $value * * @return string */ private function is_remote_value( $value ) { $value = trim( preg_replace( '/[^ -~]/xu', '', $value ) ); $wrapped_in_url = preg_match( '~^url\(\s*[\'"]\s*(.*)\s*[\'"]\s*\)$~xi', $value, $match ); if ( ! $wrapped_in_url ) { return false; } $value = trim( $match[1], '\'"' ); return preg_match( '~^((https?|ftp|file):)?//~xi', $value ); } /** * has_js_value * @param $value * * @return false|int */ private function has_js_value( $value ) { return preg_match( '/base64|data|(?:java)?script|alert\(|window\.|document/i', $value ); } /** * get_allowed_attributes * @return array */ private function get_allowed_attributes() { $allowed_attributes = [ 'class', 'clip-path', 'clip-rule', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'id', 'mask', 'opacity', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'style', 'systemlanguage', 'transform', 'href', 'xlink:href', 'xlink:title', 'cx', 'cy', 'r', 'requiredfeatures', 'clippathunits', 'type', 'rx', 'ry', 'color-interpolation-filters', 'stddeviation', 'filterres', 'filterunits', 'height', 'primitiveunits', 'width', 'x', 'y', 'font-size', 'display', 'font-family', 'font-style', 'font-weight', 'text-anchor', 'marker-end', 'marker-mid', 'marker-start', 'x1', 'x2', 'y1', 'y2', 'gradienttransform', 'gradientunits', 'spreadmethod', 'markerheight', 'markerunits', 'markerwidth', 'orient', 'preserveaspectratio', 'refx', 'refy', 'viewbox', 'maskcontentunits', 'maskunits', 'd', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'fx', 'fy', 'offset', 'stop-color', 'stop-opacity', 'xmlns', 'xmlns:se', 'xmlns:xlink', 'xml:space', 'method', 'spacing', 'startoffset', 'dx', 'dy', 'rotate', 'textlength', ]; return apply_filters( 'elementor/files/svg/allowed_attributes', $allowed_attributes ); } /** * get_allowed_elements * @return array */ private function get_allowed_elements() { $allowed_elements = [ 'a', 'circle', 'clippath', 'defs', 'style', 'desc', 'ellipse', 'fegaussianblur', 'filter', 'foreignobject', 'g', 'image', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'svg', 'switch', 'symbol', 'text', 'textpath', 'title', 'tspan', 'use', ]; return apply_filters( 'elementor/files/svg/allowed_elements', $allowed_elements ); } /** * validate_allowed_attributes * @param \DOMElement $element */ private function validate_allowed_attributes( $element ) { static $allowed_attributes = false; if ( false === $allowed_attributes ) { $allowed_attributes = $this->get_allowed_attributes(); } for ( $index = $element->attributes->length - 1; $index >= 0; $index-- ) { // get attribute name $attr_name = $element->attributes->item( $index )->name; $attr_name_lowercase = strtolower( $attr_name ); // Remove attribute if not in whitelist if ( ! in_array( $attr_name_lowercase, $allowed_attributes ) && ! $this->is_a_attribute( $attr_name_lowercase, 'aria' ) && ! $this->is_a_attribute( $attr_name_lowercase, 'data' ) ) { $element->removeAttribute( $attr_name ); continue; } $attr_value = $element->attributes->item( $index )->value; // Remove attribute if it has a remote reference or js or data-URI/base64 if ( ! empty( $attr_value ) && ( $this->is_remote_value( $attr_value ) || $this->has_js_value( $attr_value ) ) ) { $element->removeAttribute( $attr_name ); continue; } } } /** * strip_xlinks * @param \DOMElement $element */ private function strip_xlinks( $element ) { $xlinks = $element->getAttributeNS( 'http://www.w3.org/1999/xlink', 'href' ); if ( ! $xlinks ) { return; } $allowed_links = [ 'data:image/png', // PNG 'data:image/gif', // GIF 'data:image/jpg', // JPG 'data:image/jpe', // JPEG 'data:image/pjp', // PJPEG ]; if ( 1 === preg_match( self::SCRIPT_REGEX, $xlinks ) ) { if ( ! in_array( substr( $xlinks, 0, 14 ), $allowed_links ) ) { $element->removeAttributeNS( 'http://www.w3.org/1999/xlink', 'href' ); } } } /** * validate_use_tag * @param $element */ private function validate_use_tag( $element ) { $xlinks = $element->getAttributeNS( 'http://www.w3.org/1999/xlink', 'href' ); if ( $xlinks && '#' !== substr( $xlinks, 0, 1 ) ) { $element->parentNode->removeChild( $element ); // phpcs:ignore -- php DomNode } } /** * strip_docktype */ private function strip_doctype() { foreach ( $this->svg_dom->childNodes as $child ) { if ( XML_DOCUMENT_TYPE_NODE === $child->nodeType ) { // phpcs:ignore -- php DomDocument $child->parentNode->removeChild( $child ); // phpcs:ignore -- php DomDocument } } } /** * strip_php_tags * @param $string * * @return string */ private function strip_php_tags( $string ) { $string = preg_replace( '/<\?(=|php)(.+?)\?>/i', '', $string ); // Remove XML, ASP, etc. $string = preg_replace( '/<\?(.*)\?>/Us', '', $string ); $string = preg_replace( '/<\%(.*)\%>/Us', '', $string ); if ( ( false !== strpos( $string, '/Us', '', $string ); $string = preg_replace( '/\/\*(.*)\*\//Us', '', $string ); if ( ( false !== strpos( $string, '