PageRenderTime 36ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/wordpress-https/lib/WordPressHTTPS/Module/Parser.php

https://github.com/mhoofman/wordpress-heroku
PHP | 441 lines | 303 code | 41 blank | 97 comment | 176 complexity | bbc33f7b1859bf232d6e4aa889ad3e73 MD5 | raw file
  1. <?php
  2. /**
  3. * HTML Parser Module
  4. *
  5. * @author Mike Ems
  6. * @package WordPressHTTPS
  7. *
  8. */
  9. class WordPressHTTPS_Module_Parser extends Mvied_Plugin_Module {
  10. /**
  11. * HTML
  12. *
  13. * @var string
  14. */
  15. protected $_html;
  16. /**
  17. * Initialize
  18. *
  19. * @param none
  20. * @return void
  21. */
  22. public function init() {
  23. // Start output buffering
  24. add_action('init', array(&$this, 'startOutputBuffering'));
  25. }
  26. /**
  27. * Parse HTML
  28. *
  29. * Parses the output buffer to fix HTML output
  30. *
  31. * @param string $buffer
  32. * @return string $this->_html
  33. */
  34. public function parseHtml( $buffer ) {
  35. $this->_html = $buffer;
  36. $this->normalizeElements();
  37. $this->fixLinksAndForms();
  38. $this->fixExtensions();
  39. $this->fixElements();
  40. $this->fixCssElements();
  41. $this->fixRelativeElements();
  42. // Output logger contents to browsers console if in Debug Mode
  43. if ( $this->getPlugin()->getSetting('debug') == true ) {
  44. $this->consoleLog();
  45. }
  46. return $this->_html;
  47. }
  48. /**
  49. * Start output buffering
  50. *
  51. * @param none
  52. * @return void
  53. */
  54. public function startOutputBuffering() {
  55. ob_start(array(&$this, 'parseHtml'));
  56. }
  57. /**
  58. * Secure element
  59. *
  60. * @param string $url
  61. * @param string $type
  62. * @return boolean
  63. */
  64. public function secureElement( $url, $type = '' ) {
  65. $updated = false;
  66. $result = false;
  67. $upload_dir = wp_upload_dir();
  68. $upload_path = str_replace($this->getPlugin()->getHttpsUrl()->getPath(), $this->getPlugin()->getHttpUrl()->getPath(), parse_url($upload_dir['baseurl'], PHP_URL_PATH));
  69. if ( ! is_admin() || ( is_admin() && strpos($url, $upload_path) === false ) ) {
  70. $updated = $this->getPlugin()->makeUrlHttps($url);
  71. if ( $url != $updated ) {
  72. $this->_html = str_replace($url, $updated, $this->_html);
  73. } else {
  74. $updated = false;
  75. }
  76. }
  77. // Add log entry if this change hasn't been logged
  78. if ( $updated ) {
  79. $log = '[FIXED] Element: ' . ( $type != '' ? '<' . $type . '> ' : '' ) . $url . ' => ' . $updated;
  80. $result = true;
  81. } else if ( strpos($url, 'http://') === 0 ) {
  82. if ( $this->getPlugin()->getSetting('remove_unsecure') ) {
  83. $log = '[FIXED] Removed Unsecure Element: <' . $type . '> - ' . $url;
  84. } else {
  85. $log = '[WARNING] Unsecure Element: <' . $type . '> - ' . $url;
  86. }
  87. }
  88. if ( isset($log) && ! in_array($log, $this->getPlugin()->getLogger()->getLog()) ) {
  89. $this->getPlugin()->getLogger()->log($log);
  90. }
  91. return $result;
  92. }
  93. /**
  94. * Unsecure element
  95. *
  96. * @param string $url
  97. * @param string $type
  98. * @return boolean
  99. */
  100. public function unsecureElement( $url, $type = '' ) {
  101. $updated = false;
  102. $result = false;
  103. $upload_dir = wp_upload_dir();
  104. $upload_path = str_replace($this->getPlugin()->getHttpsUrl()->getPath(), $this->getPlugin()->getHttpUrl()->getPath(), parse_url($upload_dir['baseurl'], PHP_URL_PATH));
  105. if ( ! is_admin() || ( is_admin() && strpos($url, $upload_path) === false ) ) {
  106. $updated = $this->getPlugin()->makeUrlHttp($url);
  107. $this->_html = str_replace($url, $updated, $this->_html);
  108. }
  109. // Add log entry if this change hasn't been logged
  110. if ( $updated && $url != $updated ) {
  111. $log = '[FIXED] Element: ' . ( $type != '' ? '<' . $type . '> ' : '' ) . $url . ' => ' . $updated;
  112. $result = true;
  113. }
  114. if ( isset($log) && ! in_array($log, $this->getPlugin()->getLogger()->getLog()) ) {
  115. $this->getPlugin()->getLogger()->log($log);
  116. }
  117. return $result;
  118. }
  119. /**
  120. * Normalize all local URL's to HTTP
  121. *
  122. * @param none
  123. * @return void
  124. */
  125. public function normalizeElements() {
  126. $httpMatches = array();
  127. $httpsMatches = array();
  128. if ( $this->getPlugin()->getSetting('ssl_host_diff') && !is_admin() ) {
  129. $url = clone $this->getPlugin()->getHttpsUrl();
  130. $url->setScheme('http');
  131. preg_match_all('/(' . str_replace('/', '\/', preg_quote($url->toString())) . '[^\'"]*)[\'"]?/im', $this->_html, $httpsMatches);
  132. $url = clone $this->getPlugin()->getHttpUrl();
  133. $url->setScheme('https');
  134. preg_match_all('/(' . str_replace('/', '\/', preg_quote($url->toString())) . '[^\'"]*)[\'"]?/im', $this->_html, $httpMatches);
  135. $matches = array_merge($httpMatches, $httpsMatches);
  136. for ($i = 0; $i < sizeof($matches[0]); $i++) {
  137. if ( isset($matches[1][$i]) ) {
  138. $url_parts = parse_url($matches[1][$i]);
  139. if ( $url_parts && strpos($url_parts['path'], 'wp-admin') === false && strpos($url_parts['path'], 'wp-login') === false ) {
  140. $this->_html = str_replace($url, $this->getPlugin()->makeUrlHttp($url), $this->_html);
  141. }
  142. }
  143. }
  144. }
  145. }
  146. /**
  147. * Fixes schemes on DOM elements.
  148. *
  149. * @param none
  150. * @return void
  151. */
  152. public function fixElements() {
  153. if ( is_admin() ) {
  154. preg_match_all('/\<(script|link|img)[^>]+[\'"]((http|https):\/\/[^\'"]+)[\'"][^>]*>(<\/(script|link|img|input|embed|param|iframe)>\s*)?/im', $this->_html, $matches);
  155. } else {
  156. preg_match_all('/\<(script|link|img|input|embed|param|iframe)[^>]+[\'"]((http|https):\/\/[^\'"]+)[\'"][^>]*>(<\/(script|link|img|input|embed|param|iframe)>\s*)?/im', $this->_html, $matches);
  157. }
  158. for ($i = 0; $i < sizeof($matches[0]); $i++) {
  159. $html = $matches[0][$i];
  160. $type = $matches[1][$i];
  161. $url = $matches[2][$i];
  162. $scheme = $matches[3][$i];
  163. $updated = false;
  164. if ( $type == 'img' || $type == 'script' || $type == 'embed' || $type == 'iframe' ||
  165. ( $type == 'link' && ( strpos($html, 'stylesheet') !== false || strpos($html, 'pingback') !== false ) ) ||
  166. ( $type == 'form' && strpos($html, 'wp-pass.php') !== false ) ||
  167. ( $type == 'form' && strpos($html, 'wp-login.php?action=postpass') !== false ) ||
  168. ( $type == 'form' && strpos($html, 'commentform') !== false ) ||
  169. ( $type == 'input' && strpos($html, 'image') !== false ) ||
  170. ( $type == 'param' && strpos($html, 'movie') !== false )
  171. ) {
  172. if ( $this->getPlugin()->isSsl() && ( $this->getPlugin()->getSetting('ssl_host_diff') || ( !$this->getPlugin()->getSetting('ssl_host_diff') && strpos($url, 'http://') === 0 ) ) ) {
  173. if ( !$this->secureElement($url, $type) && $this->getPlugin()->getSetting('remove_unsecure') ) {
  174. $this->_html = str_replace($html, '', $this->_html);
  175. }
  176. } else if ( !$this->getPlugin()->isSsl() && strpos($url, 'https://') === 0 ) {
  177. $this->unsecureElement($url, $type);
  178. }
  179. }
  180. }
  181. }
  182. /**
  183. * Fix CSS background images or imports.
  184. *
  185. * @param none
  186. * @return void
  187. */
  188. public function fixCssElements() {
  189. preg_match_all('/(import|background)[:]?[^u]*url\([\'"]?(http:\/\/[^\'"\)]+)[\'"\)]?\)/im', $this->_html, $matches);
  190. for ($i = 0; $i < sizeof($matches[0]); $i++) {
  191. $css = $matches[0][$i];
  192. $url = $matches[2][$i];
  193. if ( $this->getPlugin()->isSsl() && ( $this->getPlugin()->getSetting('ssl_host_diff') || ( !$this->getPlugin()->getSetting('ssl_host_diff') && strpos($url, 'http://') === 0 ) ) ) {
  194. $this->secureElement($url, 'style');
  195. } else if ( !$this->getPlugin()->isSsl() && strpos($url, 'https://') === 0 ) {
  196. $this->unsecureElement($url, 'style');
  197. }
  198. }
  199. }
  200. /**
  201. * Fix elements that are being referenced relatively.
  202. *
  203. * @param none
  204. * @return void
  205. */
  206. public function fixRelativeElements() {
  207. if ( $this->getPlugin()->isSsl() && $this->getPlugin()->getHttpUrl()->getPath() != $this->getPlugin()->getHttpsUrl()->getPath() ) {
  208. preg_match_all('/\<(script|link|img|input|form|embed|param)[^>]+(src|href|action|data|movie|image|value)=[\'"](\/[^\'"]*)[\'"][^>]*>/im', $this->_html, $matches);
  209. for ($i = 0; $i < sizeof($matches[0]); $i++) {
  210. $html = $matches[0][$i];
  211. $type = $matches[1][$i];
  212. $attr = $matches[2][$i];
  213. $url_path = $matches[3][$i];
  214. if (
  215. $type != 'input' ||
  216. ( $type == 'input' && $attr == 'image' ) ||
  217. ( $type == 'input' && strpos($html, '_wp_http_referer') !== false )
  218. ) {
  219. if ( strpos($url_path, '//') !== 0 ) {
  220. $updated = clone $this->getPlugin()->getHttpsUrl();
  221. $updated->setPath($url_path);
  222. $this->_html = str_replace($html, str_replace($url_path, $updated, $html), $this->_html);
  223. $this->getPlugin()->getLogger()->log('[FIXED] Element: <' . $type . '> - ' . $url_path . ' => ' . $updated);
  224. }
  225. }
  226. }
  227. }
  228. }
  229. /**
  230. * Fixes schemes on DOM elements with extensions specified in $this->_extensions
  231. *
  232. * @param none
  233. * @return void
  234. */
  235. public function fixExtensions() {
  236. @preg_match_all('/((http|https):\/\/[^\'"\)\s]+)[\'"\)]?/i', $this->_html, $matches);
  237. for ($i = 0; $i < sizeof($matches[1]); $i++) {
  238. $url = $matches[1][$i];
  239. $filename = basename($url);
  240. $scheme = $matches[2][$i];
  241. foreach( $this->getPlugin()->getFileExtensions() as $extension ) {
  242. if ( $extension == 'js' ) {
  243. $type = 'script';
  244. } else if ( $extension == 'css' ) {
  245. $type = 'style';
  246. } else if ( in_array($extension, array('jpg', 'jpeg', 'png', 'gif')) ) {
  247. $type = 'img';
  248. } else {
  249. continue;
  250. }
  251. if ( preg_match('/\.' . $extension . '(\?|$)/', $filename) ) {
  252. if ( $this->getPlugin()->isSsl() && ( $this->getPlugin()->getSetting('ssl_host_diff') || ( !$this->getPlugin()->getSetting('ssl_host_diff') && strpos($url, 'http://') === 0 ) ) ) {
  253. $this->secureElement($url, $type);
  254. } else if ( !$this->getPlugin()->isSsl() && strpos($url, 'https://') === 0 ) {
  255. $this->unsecureElement($url, $type);
  256. }
  257. }
  258. }
  259. }
  260. }
  261. /**
  262. * Fix links and forms
  263. *
  264. * @param none
  265. * @return void
  266. */
  267. public function fixLinksAndForms() {
  268. global $wpdb;
  269. // Update anchor and form tags to appropriate URL's
  270. preg_match_all('/\<(a|form)[^>]+[\'"]((http|https):\/\/[^\'"]+)[\'"][^>]*>/im', $this->_html, $matches);
  271. for ($i = 0; $i < sizeof($matches[0]); $i++) {
  272. $html = $matches[0][$i];
  273. $type = $matches[1][$i];
  274. $url = $matches[2][$i];
  275. $scheme = $matches[3][$i];
  276. $updated = false;
  277. $post_id = null;
  278. $blog_id = null;
  279. $force_ssl = null;
  280. $url_path = '/';
  281. if ( !$this->getPlugin()->isUrlLocal($url) ) {
  282. continue;
  283. }
  284. if ( $url != '' && ($url_parts = parse_url($url)) && isset($url_parts['path']) ) {
  285. if ( $this->getPlugin()->getHttpsUrl()->getPath() != '/' ) {
  286. if ( $this->getPlugin()->getSetting('ssl_host_diff') ) {
  287. $url_parts['path'] = str_replace($this->getPlugin()->getHttpsUrl()->getPath(), '', $url_parts['path']);
  288. }
  289. if ( $this->getPlugin()->getHttpUrl()->getPath() != '/' ) {
  290. $url_parts['path'] = str_replace($this->getPlugin()->getHttpUrl()->getPath(), '', $url_parts['path']);
  291. }
  292. }
  293. // qTranslate integration - strips language from beginning of url path
  294. if ( defined('QTRANS_INIT') && constant('QTRANS_INIT') == true ) {
  295. global $q_config;
  296. if ( isset($q_config['enabled_languages']) ) {
  297. foreach($q_config['enabled_languages'] as $language) {
  298. $url_parts['path'] = preg_replace('/^\/' . $language . '\//', '/', $url_parts['path']);
  299. }
  300. }
  301. }
  302. if ( preg_match("/page_id=([\d]+)/", parse_url($url, PHP_URL_QUERY), $postID) ) {
  303. $post_id = $postID[1];
  304. } else if ( isset($url_parts['path']) && ( $url_parts['path'] == '' || $url_parts['path'] == '/' ) ) {
  305. if ( get_option('show_on_front') == 'page' ) {
  306. $post_id = get_option('page_on_front');
  307. }
  308. } else if ( isset($url_parts['path']) && ($post = get_page_by_path($url_parts['path'])) ) {
  309. $post_id = $post->ID;
  310. }
  311. if ( is_multisite() && isset($url_parts['host']) ) {
  312. if ( is_subdomain_install() ) {
  313. $blog_id = get_blog_id_from_url( $url_parts['host'], '/');
  314. } else {
  315. $url_path_segments = explode('/', $url_parts['path']);
  316. if ( sizeof($url_path_segments) > 1 ) {
  317. foreach( $url_path_segments as $url_path_segment ) {
  318. if ( is_null($blog_id) && $url_path_segment != '' ) {
  319. $url_path .= $url_path_segment . '/';
  320. if ( ($blog_id = get_blog_id_from_url( $url_parts['host'], $url_path)) > 0 ) {
  321. break;
  322. } else {
  323. $blog_id = null;
  324. }
  325. }
  326. }
  327. }
  328. }
  329. if ( !is_null($blog_id) && $blog_id != $wpdb->blogid ) {
  330. // URL Filters
  331. if ( sizeof((array)$this->getPlugin()->getSetting('secure_filter', $blog_id)) > 0 ) {
  332. foreach( $this->getPlugin()->getSetting('secure_filter', $blog_id) as $filter ) {
  333. if ( preg_match('/' . str_replace('/', '\/', $filter) . '/', $url) === 1 ) {
  334. $force_ssl = true;
  335. }
  336. }
  337. }
  338. if ( ( $this->getPlugin()->getSetting('ssl_admin', $blog_id) || defined('FORCE_SSL_ADMIN') && constant('FORCE_SSL_ADMIN') ) && strpos($url_parts['path'], 'wp-admin') !== false && ( ! $this->getPlugin()->getSetting('ssl_host_diff', $blog_id) || ( $this->getPlugin()->getSetting('ssl_host_diff', $blog_id) && function_exists('is_user_logged_in') && is_user_logged_in() ) ) ) {
  339. $force_ssl = true;
  340. } else if ( is_null($force_ssl) && $this->getPlugin()->getSetting('exclusive_https', $blog_id) ) {
  341. $force_ssl = false;
  342. } else if ( strpos($url, 'https://') === 0 ) {
  343. $force_ssl = true;
  344. }
  345. }
  346. }
  347. }
  348. // Only apply force_ssl filters for current blog
  349. if ( is_null($blog_id) ) {
  350. $force_ssl = apply_filters('force_ssl', null, ( isset($post_id) ? $post_id : 0 ), $url );
  351. }
  352. if ( $force_ssl == true ) {
  353. if ( is_null($blog_id) ) {
  354. $updated = $this->getPlugin()->makeUrlHttps($url);
  355. } else {
  356. if ( $this->getPlugin()->getSetting('ssl_host', $blog_id) ) {
  357. $ssl_host = $this->getPlugin()->getSetting('ssl_host', $blog_id);
  358. } else {
  359. $ssl_host = parse_url(get_home_url($blog_id, '/'), PHP_URL_HOST);
  360. }
  361. if ( is_subdomain_install() ) {
  362. $host = $url_parts['host'] . '/';
  363. } else {
  364. $host = $url_parts['host'] . '/' . $url_path;
  365. }
  366. $updated = str_replace($url_parts['scheme'] . '://' . $host, $ssl_host, $url);
  367. }
  368. $this->_html = str_replace($html, str_replace($url, $updated, $html), $this->_html);
  369. } else if ( !is_null($force_ssl) && !$force_ssl ) {
  370. if ( is_null($blog_id) ) {
  371. $updated = $this->getPlugin()->makeUrlHttp($url);
  372. } else {
  373. if ( is_subdomain_install() ) {
  374. $host = $url_parts['host'] . '/';
  375. } else {
  376. $host = $url_parts['host'] . '/' . $url_path;
  377. }
  378. $updated = str_replace($url_parts['scheme'] . '://' . $host, get_home_url($blog_id, '/'), $url);
  379. }
  380. $this->_html = str_replace($html, str_replace($url, $updated, $html), $this->_html);
  381. }
  382. // Add log entry if this change hasn't been logged
  383. if ( $updated && $url != $updated ) {
  384. $log = '[FIXED] Element: <' . $type . '> - ' . $url . ' => ' . $updated;
  385. if ( ! in_array($log, $this->getPlugin()->getLogger()->getLog()) ) {
  386. $this->getPlugin()->getLogger()->log($log);
  387. }
  388. }
  389. }
  390. }
  391. /**
  392. * Output contents of the log to the browser's console.
  393. *
  394. * @param none
  395. * @return void
  396. */
  397. public function consoleLog() {
  398. $this->_html = str_replace('</body>', $this->getPlugin()->getLogger()->consoleLog() . "\n\n</body>", $this->_html);
  399. }
  400. }