PluginProbe ʕ •ᴥ•ʔ
Presto Player / 4.2.4
Presto Player v4.2.4
4.3.0 4.2.4 4.2.3 4.2.2 4.2.0 4.2.1 trunk 1.10.0 1.10.1 1.10.2 1.11.0 1.12.0 1.13.0 1.14.0 1.14.1 1.5.10 1.5.11 1.5.12 1.5.13 1.5.14 1.5.15 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.10 1.6.11 1.6.12 1.6.13 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.8.0 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.11 2.0.12 2.0.13 2.0.14 2.0.15 2.0.16 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.2.0 2.2.1 2.2.2 2.2.3 2.2.3-beta1 2.3.0 2.3.1 2.3.2 2.3.3 3.0.0 3.0.0-beta1 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.1.0 3.1.1 3.1.2 3.1.3 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.0.7 4.0.8 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4
presto-player / inc / Attachment.php
presto-player / inc Last commit date
Blocks 2 months ago Contracts 1 year ago Database 1 week ago Integrations 1 week ago Libraries 1 week ago Models 1 week ago Seeds 1 year ago Services 1 week ago Support 1 week ago config 1 week ago lib 1 month ago Activator.php 1 month ago Attachment.php 1 week ago Controller.php 1 year ago Core.php 1 year ago Deactivator.php 2 months ago Factory.php 3 months ago Files.php 1 week ago Playlist.php 1 year ago Plugin.php 1 month ago Requirements.php 1 year ago support.php 1 week ago
Attachment.php
328 lines
1 <?php
2 /**
3 * Private/protected video attachment handling.
4 *
5 * @package PrestoPlayer
6 */
7
8 namespace PrestoPlayer;
9
10 use PrestoPlayer\Services\AdminNotices;
11 use PrestoPlayer\Services\Streamer;
12
13 /**
14 * Handles private/protected video attachment streaming and access.
15 */
16 class Attachment {
17
18 /**
19 * Whether the premium version is active.
20 *
21 * @var bool
22 */
23 protected $is_premium;
24
25 /**
26 * Constructor.
27 *
28 * @param bool $is_premium Whether the premium version is active.
29 */
30 public function __construct( $is_premium = false ) {
31 $this->is_premium = $is_premium;
32 }
33
34 /**
35 * Registers WordPress hooks.
36 *
37 * @return Attachment
38 */
39 public function register() {
40 if ( $this->is_premium ) {
41 add_action( 'admin_notices', array( $this, 'checkServer' ) );
42 }
43 add_action( 'wp_get_attachment_url', array( $this, 'replaceLink' ), 10, 2 );
44 add_action( 'query_vars', array( $this, 'addQueryVars' ) );
45 add_action( 'generate_rewrite_rules', array( $this, 'customRewriteRules' ) );
46 add_action( 'template_redirect', array( $this, 'loadVirtualPage' ) );
47 add_action( 'wp_ajax_presto_player_load_user_video', array( $this, 'refreshAjaxTempSecurityUser' ) );
48
49 return $this;
50 }
51
52 /**
53 * Refreshes the temporary security user transient via AJAX.
54 *
55 * @param string $action The AJAX action hook name.
56 * @return void
57 */
58 public function refreshAjaxTempSecurityUser( $action ) {
59 $type = isset( $_POST['type'] ) ? sanitize_text_field( wp_unslash( $_POST['type'] ) ) : '';
60
61 if ( empty( $type ) ) {
62 wp_send_json_error( 'type not set' );
63 }
64
65 if ( ! defined( 'DOING_AJAX' ) && ! is_user_logged_in() ) {
66 wp_safe_redirect( home_url() );
67 exit();
68 }
69
70 check_ajax_referer( 'presto_player' );
71
72 if ( 'private-hosted' === $type ) {
73 if ( isset( $_POST['id'] ) ) {
74 $post_id = (int) $_POST['id'];
75 $this->setVideoTransient( (int) $post_id );
76 wp_send_json_success( $this->getSrc( (int) $post_id, true ) );
77 }
78 }
79
80 if ( ! $this->is_premium ) {
81 wp_send_json_success();
82 return;
83 }
84
85 wp_send_json_success();
86 }
87
88 /**
89 * Gets the transient key for the current user's video access list.
90 *
91 * @return string
92 */
93 public function getTransientKey() {
94 if ( ! function_exists( 'wp_get_current_user' ) ) {
95 return '';
96 }
97 $current_user = \wp_get_current_user();
98 return 'presto-player-user-' . $current_user->ID;
99 }
100
101 /**
102 * Adds query vars for rewrites
103 *
104 * @param array $query_vars Existing registered query vars.
105 * @return array
106 */
107 public function addQueryVars( $query_vars ) {
108 $query_vars[] = 'presto-player-video';
109 $query_vars[] = 'presto-player-token';
110 return $query_vars;
111 }
112
113 /**
114 * Add custom rewrite rules
115 *
116 * @param \WP_Rewrite $wp_rewrite The WP_Rewrite instance.
117 * @return void
118 */
119 public function customRewriteRules( $wp_rewrite ) {
120 $wp_rewrite->rules = array_merge(
121 array( 'video-src/([^/]*)/(\d+)/?$' => 'index.php?presto-player-token=$matches[1]&presto-player-video=$matches[2]' ),
122 $wp_rewrite->rules
123 );
124 }
125
126 /**
127 * Load virtual template to stream video by id
128 */
129 public function loadVirtualPage() {
130 // get video attachment id.
131 $video_id = intval( get_query_var( 'presto-player-video' ) );
132 // get the token.
133 $token = sanitize_text_field( get_query_var( 'presto-player-token' ) );
134
135 if ( $video_id && $token ) {
136 if ( ! is_user_logged_in() ) {
137 wp_die( 'Access denied! :(', 'Access Denied', array( 'response' => 403 ) );
138 }
139 $this->checkAndLoadStream( wp_get_current_user(), $video_id, $token );
140 die;
141 }
142 }
143
144 /**
145 * Check the server
146 *
147 * @return void
148 */
149 public function checkServer() {
150 // check for nginx.
151 $notice_name = 'nginx_rules';
152 $server_software = isset( $_SERVER['SERVER_SOFTWARE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : false;
153 if ( ! stristr( $server_software, 'nginx' ) ) {
154 return;
155 }
156
157 if ( current_user_can( 'install_plugins' ) && ! AdminNotices::isDismissed( $notice_name ) ) {
158 $this->showNotice( $notice_name );
159 }
160 }
161
162 /**
163 * Renders the NGINX protection admin notice.
164 *
165 * @param string $notice_name The notice identifier.
166 * @return void
167 */
168 public function showNotice( $notice_name ) {
169 ob_start(); ?>
170
171 <div class="error">
172 <h3>Presto Player</h3>
173 <p><?php /* translators: %s: Name of the protected uploads folder. */ printf( esc_html__( 'The video files in the %s folder are not currently protected due to your site running on NGINX.', 'presto-player' ), '<strong>presto-player-private</strong>' ); ?></p>
174 <p><?php echo wp_kses_post( __( 'If you plan on using private video, you will want to protect this directory. To protect them, you must add a firewall rule as explained in <a href="https://prestoplayer.com/protecting-videos-with-nginx" target="_blank">this guide</a>.', 'presto-player' ) ); ?></p>
175 <p><?php esc_html_e( 'If you have already added the rule, you may safely dismiss this notice', 'presto-player' ); ?></p>
176 <p><a href="
177 <?php
178 echo esc_url(
179 add_query_arg(
180 array(
181 'presto_action' => 'dismiss_notices',
182 'presto_notice' => $notice_name,
183 '_wpnonce' => wp_create_nonce( 'presto-dismiss-notice' ),
184 )
185 )
186 );
187 ?>
188 "><?php esc_html_e( 'Dismiss Notice', 'presto-player' ); ?></a></p>
189 </div>
190
191 <?php
192 echo ob_get_clean(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Notice markup is composed of the internally escaped strings above.
193 }
194
195 /**
196 * Sets the transient for video access
197 * Sets this for 24 hours
198 *
199 * @param integer $post_id The attachment post ID.
200 * @return void
201 */
202 public function setVideoTransient( $post_id ) {
203 $videos = (array) get_transient( $this->getTransientKey() );
204 $videos[] = sanitize_text_field( $post_id );
205
206 // set temporary user transient for access for 1 hour.
207 set_transient( $this->getTransientKey(), array_filter( array_unique( $videos ) ), 24 * HOUR_IN_SECONDS );
208 }
209
210 /**
211 * Gets the source URL for an attachment.
212 *
213 * @param int $id The attachment ID.
214 * @param bool $private Whether to return the private (protected) URL.
215 * @return string|false
216 */
217 public static function getSrc( $id, $private = false ) {
218 if ( $private ) {
219 return self::getPrivateSrc( $id );
220 }
221 return wp_get_attachment_url( $id );
222 }
223
224 /**
225 * Gets the public (unprotected) source URL for an attachment.
226 *
227 * @param int $id The attachment ID.
228 * @return string|false
229 */
230 public static function getPublicSrc( $id ) {
231 global $presto_override_private_url;
232 $old = $presto_override_private_url;
233 $presto_override_private_url = true;
234 $url = wp_get_attachment_url( $id );
235 $presto_override_private_url = $old;
236 return $url;
237 }
238
239 /**
240 * Determines whether an attachment is private.
241 *
242 * @param int $id The attachment ID.
243 * @return int|false Position of the marker in the URL, or false if public.
244 */
245 public static function isPrivate( $id ) {
246 return strpos( wp_get_attachment_url( $id ), 'video-src' );
247 }
248
249 /**
250 * Gets the private (token-protected) source URL for an attachment.
251 *
252 * @param int $id The attachment ID.
253 * @return string
254 */
255 public static function getPrivateSrc( $id ) {
256 if ( ! function_exists( 'wp_create_nonce' ) ) {
257 return '';
258 }
259 // set temporary user transient for access for 1 hour.
260 ( new self() )->setVideoTransient( $id );
261 if ( ! get_option( 'permalink_structure' ) ) {
262 return sprintf( site_url( '?presto-player-video=%d&presto-player-token=%s' ), $id, wp_create_nonce( 'presto-player-user-token' ) );
263 }
264 return sprintf( site_url( 'video-src/%s/%d' ), wp_create_nonce( 'presto-player-user-token' ), $id );
265 }
266
267 /**
268 * Replaces attachment link
269 *
270 * @param string $url The attachment URL.
271 * @param int $post_id The attachment post ID.
272 * @return string
273 */
274 public function replaceLink( $url, $post_id ) {
275 global $presto_override_private_url;
276
277 // only replace for our folder.
278 if ( ! stristr( $url, 'presto-player-private' ) ) {
279 return $url;
280 }
281
282 if ( ! $presto_override_private_url ) {
283 return self::getPrivateSrc( $post_id );
284 } else {
285 return $url;
286 }
287 }
288
289 /**
290 * Check and load stream through PHP
291 *
292 * @param \WP_User $current_user The current user object.
293 * @param integer $attachment_id The attachment post ID.
294 * @param string $token The security token.
295 * @return void
296 */
297 public function checkAndLoadStream( $current_user, $attachment_id, $token ) {
298 $security_token = isset( $token ) ? wp_verify_nonce( $token, 'presto-player-user-token' ) : false;
299 $temp_security_user = get_transient( $this->getTransientKey() );
300
301 /**
302 * Start video stream with the correct video SRC only in case of pass security rules
303 */
304 if ( $security_token && $temp_security_user && $attachment_id > 0 && in_array( $attachment_id, $temp_security_user ) ) {
305 $video_file = get_attached_file( $attachment_id );
306 $file_type = wp_check_filetype( $video_file );
307
308 /**
309 * Start video stream to show the video
310 */
311 $video_stream = new Streamer( $video_file, $file_type['type'] );
312 $video_stream->start();
313 exit();
314 } else {
315
316 /**
317 * Alert user about the misconduct by accessing directly
318 */
319 $message = sprintf(
320 /* translators: %1$s: Current user display name. */
321 __( 'Sorry %1$s! Access to this video is not allowed. An administrator will be informed.', 'presto-player' ),
322 ucfirst( $current_user->display_name )
323 );
324 wp_die( esc_html( $message ), esc_html__( 'Forbidden', 'presto-player' ), 403 );
325 }
326 }
327 }
328