PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.2
Jetpack – WP Security, Backup, Speed, & Growth v16.2
16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 All 506 releases
jetpack / jetpack_vendor / automattic / jetpack-publicize / src / class-keyring-helper.php

class-keyring-helper.php in Jetpack – WP Security, Backup, Speed, & Growth 16.2, at jetpack_vendor/automattic/jetpack-publicize/src/class-keyring-helper.php

296 lines 8.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Keyring helper.
4 *
5 * @package automattic/jetpack-publicize
6 */
7
8 namespace Automattic\Jetpack\Publicize;
9
10 use Automattic\Jetpack\Connection\Secrets;
11 use Automattic\Jetpack\Paths;
12 use Jetpack_IXR_Client;
13 use Jetpack_Options;
14
15 /**
16 * A series of utilities to interact with a Keyring instance.
17 */
18 class Keyring_Helper {
19 /**
20 * Class instance
21 *
22 * @var Keyring_Helper
23 */
24 private static $instance = null;
25
26 /**
27 * Whether the `sharing` page is registered.
28 *
29 * @var bool
30 */
31 private static $is_sharing_page_registered = false;
32
33 /**
34 * Initialize instance.
35 */
36 public static function init() {
37 if ( null === self::$instance ) {
38 self::$instance = new Keyring_Helper();
39 }
40
41 return self::$instance;
42 }
43
44 const SERVICES = array(
45 'facebook' => array(
46 'for' => 'publicize',
47 ),
48 'twitter' => array(
49 'for' => 'publicize',
50 ),
51 'linkedin' => array(
52 'for' => 'publicize',
53 ),
54 'tumblr' => array(
55 'for' => 'publicize',
56 ),
57 'path' => array(
58 'for' => 'publicize',
59 ),
60 'google_plus' => array(
61 'for' => 'publicize',
62 ),
63 'google_site_verification' => array(
64 'for' => 'other',
65 ),
66 );
67
68 /**
69 * Constructor
70 */
71 private function __construct() {
72 add_action( 'admin_init', array( __CLASS__, 'intercept_request' ) );
73 }
74
75 /**
76 * Gets a URL to the public-api actions. Works like WP's admin_url.
77 * On WordPress.com this is/calls Keyring::admin_url.
78 *
79 * @param string $service Shortname of a specific service.
80 * @param array $params Parameters to append to an API connection URL.
81 *
82 * @return string URL to specific public-api process
83 */
84 private static function api_url( $service = false, $params = array() ) {
85 /**
86 * Filters the API URL used to interact with WordPress.com.
87 *
88 * @since 0.1.0
89 * @since-jetpack 2.0.0
90 *
91 * @param string https://public-api.wordpress.com/connect/?jetpack=publicize Default Publicize API URL.
92 */
93 $url = apply_filters( 'publicize_api_url', 'https://public-api.wordpress.com/connect/?jetpack=publicize' );
94
95 if ( $service ) {
96 $url = add_query_arg( array( 'service' => $service ), $url );
97 }
98
99 if ( array() !== $params ) {
100 $url = add_query_arg( $params, $url );
101 }
102
103 return $url;
104 }
105
106 /**
107 * Build a connection URL (sharing settings page with unique query args to create a connection).
108 *
109 * @param string $service_name Service name.
110 * @param string $for Feature name.
111 */
112 public static function connect_url( $service_name, $for ) {
113 return add_query_arg(
114 array(
115 'action' => 'request',
116 'service' => $service_name,
117 'kr_nonce' => wp_create_nonce( 'keyring-request' ),
118 'nonce' => wp_create_nonce( "keyring-request-$service_name" ),
119 'for' => $for,
120 'publicize_action' => 1,
121 ),
122 admin_url()
123 );
124 }
125
126 /**
127 * Build a URL to refresh a connection (sharing settings page with unique query args to refresh a connection).
128 * Similar to connect_url, but with a refresh parameter.
129 *
130 * @param string $service_name Service name.
131 * @param string $for Feature name.
132 */
133 public static function refresh_url( $service_name, $for ) {
134 return add_query_arg(
135 array(
136 'action' => 'request',
137 'service' => $service_name,
138 'kr_nonce' => wp_create_nonce( 'keyring-request' ),
139 'refresh' => 1,
140 'for' => $for,
141 'nonce' => wp_create_nonce( "keyring-request-$service_name" ),
142 'publicize_action' => 1,
143 ),
144 admin_url()
145 );
146 }
147
148 /**
149 * Build a URL to delete a connection (sharing settings page with unique query args to delete a connection).
150 *
151 * @param string $service_name Service name.
152 * @param string $id Connection ID.
153 */
154 public static function disconnect_url( $service_name, $id ) {
155 return add_query_arg(
156 array(
157 'action' => 'delete',
158 'service' => $service_name,
159 'id' => $id,
160 'kr_nonce' => wp_create_nonce( 'keyring-request' ),
161 'nonce' => wp_create_nonce( "keyring-request-$service_name" ),
162 'publicize_action' => 1,
163 ),
164 admin_url()
165 );
166 }
167
168 /**
169 * Build contents handling Keyring connection management into Sharing settings screen.
170 */
171 public static function intercept_request() {
172 if ( ! empty( $_GET['publicize_action'] ) && isset( $_GET['action'] ) ) {
173 $service_name = null;
174
175 if ( isset( $_GET['service'] ) ) {
176 $service_name = filter_var( wp_unslash( $_GET['service'] ) );
177 }
178
179 switch ( $_GET['action'] ) {
180
181 case 'request':
182 check_admin_referer( 'keyring-request', 'kr_nonce' );
183 check_admin_referer( "keyring-request-$service_name", 'nonce' );
184
185 $verification = ( new Secrets() )->generate( 'publicize' );
186 if ( ! $verification ) {
187 $url = ( new Paths() )->admin_url( 'page=jetpack#/settings' );
188 wp_die(
189 sprintf(
190 wp_kses(
191 /* Translators: placeholder is a URL to a Settings page. */
192 __( "Jetpack is not connected. Please connect Jetpack by visiting <a href='%s'>Settings</a>.", 'jetpack-publicize-pkg' ),
193 array(
194 'a' => array(
195 'href' => array(),
196 ),
197 )
198 ),
199 esc_url( $url )
200 )
201 );
202
203 }
204 $stats_options = get_option( 'stats_options' );
205 $wpcom_blog_id = Jetpack_Options::get_option( 'id' );
206 $wpcom_blog_id = ! empty( $wpcom_blog_id ) ? $wpcom_blog_id : $stats_options['blog_id'];
207
208 $for = isset( $_GET['for'] ) ? sanitize_text_field( wp_unslash( $_GET['for'] ) ) : 'publicize';
209
210 $custom_inputs = array();
211
212 // For Bluesky.
213 if ( isset( $_GET['handle'] ) && isset( $_GET['app_password'] ) ) {
214 $custom_inputs['handle'] = sanitize_text_field( wp_unslash( $_GET['handle'] ) );
215
216 $custom_inputs['app_password'] = sanitize_text_field( wp_unslash( $_GET['app_password'] ) );
217 }
218
219 // For Mastodon.
220 if ( isset( $_GET['instance'] ) ) {
221 $custom_inputs['instance'] = sanitize_text_field( wp_unslash( $_GET['instance'] ) );
222 }
223
224 $user = wp_get_current_user();
225 $redirect = self::api_url(
226 $service_name,
227 urlencode_deep(
228 $custom_inputs +
229 array(
230 'action' => 'request',
231 'redirect_uri' => add_query_arg( array( 'action' => 'done' ), menu_page_url( 'sharing', false ) ),
232 'for' => $for,
233 // required flag that says this connection is intended for publicize.
234 'siteurl' => site_url(),
235 'state' => $user->ID,
236 'blog_id' => $wpcom_blog_id,
237 'secret_1' => $verification['secret_1'],
238 'secret_2' => $verification['secret_2'],
239 'eol' => $verification['exp'],
240 )
241 )
242 );
243 wp_redirect( $redirect ); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect -- The API URL is an external URL and is filterable.
244 exit( 0 );
245
246 case 'completed':
247 /*
248 * We do not use a nonce here,
249 * since we're populating a local cache of
250 * the Publicize connections that were created and stored on WordPress.com.
251 */
252 $xml = new Jetpack_IXR_Client();
253 $xml->query( 'jetpack.fetchPublicizeConnections' );
254
255 if ( ! $xml->isError() ) {
256 $response = $xml->getResponse();
257 Jetpack_Options::update_option( 'publicize_connections', $response );
258 }
259
260 break;
261
262 case 'delete':
263 $id = isset( $_GET['id'] ) ? filter_var( wp_unslash( $_GET['id'] ) ) : null;
264
265 check_admin_referer( 'keyring-request', 'kr_nonce' );
266 check_admin_referer( "keyring-request-$service_name", 'nonce' );
267
268 self::disconnect( $service_name, $id );
269
270 do_action( 'connection_disconnected', $service_name );
271 break;
272 }
273 }
274 }
275
276 /**
277 * Remove a Publicize connection
278 *
279 * @param string $service_name Service name.
280 * @param string $connection_id Connection ID.
281 * @param int|bool $_blog_id Blog ID.
282 * @param int|bool $_user_id User ID.
283 * @param bool $force_delete Force delete the connection.
284 */
285 public static function disconnect( $service_name, $connection_id, $_blog_id = false, $_user_id = false, $force_delete = false ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
286 $xml = new Jetpack_IXR_Client();
287 $xml->query( 'jetpack.deletePublicizeConnection', $connection_id );
288
289 if ( ! $xml->isError() ) {
290 Jetpack_Options::update_option( 'publicize_connections', $xml->getResponse() );
291 } else {
292 return false;
293 }
294 }
295 }
296