<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[31614] trunk: In `wp_get_attachment_url()`, convert to HTTPS when possible.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/31614">31614</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/31614","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>boonebgorges</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2015-03-05 02:38:59 +0000 (Thu, 05 Mar 2015)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>In `wp_get_attachment_url()`, convert to HTTPS when possible.

`wp_get_attachment_url()`, via `wp_upload_dir()`, uses 'siteurl' to generate
attachment URLs. When a site is SSL-optional on the front end - ie, 'siteurl'
is non-HTTPS, but SSL is available - a number of situations can arise where
non-HTTPS attachment URLs cause browser mixed-content warnings:

a) SSL is forced in the admin and `wp_get_attachment_url()` is used to generate the `<img>` tag for an inserted image. In these cases, the post content will contain non-HTTPS. Viewing/editing this post in the Dashboard will result in non-HTTPS images being served in an SSL environment.
b) `wp_get_attachment_url()` is used in a theme to generate an `<img>` `src` attribute on a public page. When viewing that page over SSL, the images will have HTTP URLs.

This changeset switches attachment URLs to HTTPS when it's determined that the
host supports SSL. This happens when 'siteurl' is non-SSL, but the current page
request *is* over SSL, and the host of the current request matches the host of
the URL being generated.

Props joemcgill, boonebgorges.
Fixes <a href="https://core.trac.wordpress.org/ticket/15928">#15928</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpincludespostphp">trunk/src/wp-includes/post.php</a></li>
<li><a href="#trunktestsphpunittestspostattachmentsphp">trunk/tests/phpunit/tests/post/attachments.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpincludespostphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/post.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/post.php    2015-03-04 21:00:52 UTC (rev 31613)
+++ trunk/src/wp-includes/post.php      2015-03-05 02:38:59 UTC (rev 31614)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4980,6 +4980,14 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $url = get_the_guid( $post->ID );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        /*
+        * If currently on SSL, prefer HTTPS URLs when we know they're supported by the domain
+        * (which is to say, when they share the domain name of the current SSL page).
+        */
+       if ( is_ssl() && 'https' !== substr( $url, 0, 5 ) && parse_url( $url, PHP_URL_HOST ) === $_SERVER['HTTP_HOST'] ) {
+               $url = set_url_scheme( $url, 'https' );
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         /**
</span><span class="cx" style="display: block; padding: 0 10px">         * Filter the attachment URL.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span></span></pre></div>
<a id="trunktestsphpunittestspostattachmentsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/post/attachments.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/post/attachments.php    2015-03-04 21:00:52 UTC (rev 31613)
+++ trunk/tests/phpunit/tests/post/attachments.php      2015-03-05 02:38:59 UTC (rev 31614)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -278,4 +278,165 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertEquals( $attachment->post_parent, $post_id );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        /**
+        * @ticket 15928
+        */
+       public function test_wp_get_attachment_url_should_not_force_https_when_current_page_is_non_ssl_and_siteurl_is_non_ssl() {
+               $siteurl = get_option( 'siteurl' );
+               update_option( 'siteurl', set_url_scheme( $siteurl, 'http' ) );
+
+               $filename = DIR_TESTDATA . '/images/test-image.jpg';
+               $contents = file_get_contents( $filename );
+
+               $upload = wp_upload_bits( basename( $filename ), null, $contents );
+               $this->assertTrue( empty( $upload['error'] ) );
+
+               // Set attachment ID.
+               $attachment_id = $this->_make_attachment( $upload );
+
+               // Save server data for cleanup.
+               $is_ssl = is_ssl();
+               $_SERVER['HTTPS'] = 'off';
+
+               $url = wp_get_attachment_url( $attachment_id );
+               $this->assertSame( set_url_scheme( $url, 'http' ), $url );
+
+               // Cleanup.
+               $_SERVER['HTTPS'] = $is_ssl ? 'on' : 'off';
+       }
+
+       /**
+        * @ticket 15928
+        *
+        * This situation (current request is non-SSL but siteurl is https) should never arise.
+        */
+       public function test_wp_get_attachment_url_should_not_force_https_when_current_page_is_non_ssl_and_siteurl_is_ssl() {
+               $siteurl = get_option( 'siteurl' );
+               update_option( 'siteurl', set_url_scheme( $siteurl, 'https' ) );
+
+               $filename = DIR_TESTDATA . '/images/test-image.jpg';
+               $contents = file_get_contents( $filename );
+
+               $upload = wp_upload_bits( basename( $filename ), null, $contents );
+               $this->assertTrue( empty( $upload['error'] ) );
+
+               // Set attachment ID.
+               $attachment_id = $this->_make_attachment( $upload );
+
+               // Save server data for cleanup.
+               $is_ssl = is_ssl();
+               $_SERVER['HTTPS'] = 'off';
+
+               $url = wp_get_attachment_url( $attachment_id );
+               $this->assertSame( set_url_scheme( $url, 'http' ), $url );
+
+               // Cleanup.
+               $_SERVER['HTTPS'] = $is_ssl ? 'on' : 'off';
+       }
+
+       /**
+        * @ticket 15928
+        *
+        * Canonical siteurl is non-SSL, but SSL support is available/optional.
+        */
+       public function test_wp_get_attachment_url_should_force_https_with_https_on_same_host_when_siteurl_is_non_ssl_but_ssl_is_available() {
+               $siteurl = get_option( 'siteurl' );
+               update_option( 'siteurl', set_url_scheme( $siteurl, 'http' ) );
+
+               $filename = ( DIR_TESTDATA . '/images/test-image.jpg' );
+               $contents = file_get_contents( $filename );
+
+               $upload = wp_upload_bits( basename( $filename ), null, $contents );
+               $this->assertTrue( empty( $upload['error'] ) );
+
+               // Set attachment ID
+               $attachment_id = $this->_make_attachment( $upload );
+
+               // Save server data for cleanup
+               $is_ssl = is_ssl();
+               $http_host = $_SERVER['HTTP_HOST'];
+
+               $_SERVER['HTTPS'] = 'on';
+
+               // Verify that server host matches the host of wp_upload_dir().
+               $upload_dir = wp_upload_dir();
+               $_SERVER['HTTP_HOST'] = parse_url( $upload_dir['baseurl'], PHP_URL_HOST );
+
+               // Test that wp_get_attachemt_url returns with https scheme.
+               $url = wp_get_attachment_url( $attachment_id );
+               $this->assertSame( set_url_scheme( $url, 'https' ), $url );
+
+               // Cleanup.
+               $_SERVER['HTTPS'] = $is_ssl ? 'on' : 'off';
+               $_SERVER['HTTP_HOST'] = $http_host;
+       }
+
+       /**
+        * @ticket 15928
+        */
+       public function test_wp_get_attachment_url_with_https_on_same_host_when_siteurl_is_https() {
+               $siteurl = get_option( 'siteurl' );
+               update_option( 'siteurl', set_url_scheme( $siteurl, 'https' ) );
+
+               $filename = ( DIR_TESTDATA . '/images/test-image.jpg' );
+               $contents = file_get_contents( $filename );
+
+               $upload = wp_upload_bits( basename( $filename ), null, $contents );
+               $this->assertTrue( empty( $upload['error'] ) );
+
+               // Set attachment ID.
+               $attachment_id = $this->_make_attachment( $upload );
+
+               // Save server data for cleanup.
+               $is_ssl = is_ssl();
+               $http_host = $_SERVER['HTTP_HOST'];
+
+               $_SERVER['HTTPS'] = 'on';
+
+               // Verify that server host matches the host of wp_upload_dir().
+               $upload_dir = wp_upload_dir();
+               $_SERVER['HTTP_HOST'] = parse_url( $upload_dir['baseurl'], PHP_URL_HOST );
+
+               // Test that wp_get_attachemt_url returns with https scheme.
+               $url = wp_get_attachment_url( $attachment_id );
+               $this->assertSame( set_url_scheme( $url, 'https' ), $url );
+
+               // Cleanup.
+               $_SERVER['HTTPS'] = $is_ssl ? 'on' : 'off';
+               $_SERVER['HTTP_HOST'] = $http_host;
+       }
+
+       /**
+       * @ticket 15928
+       */
+       public function test_wp_get_attachment_url_should_not_force_https_when_https_is_on_but_url_has_a_different_domain() {
+               $siteurl = get_option( 'siteurl' );
+               update_option( 'siteurl', set_url_scheme( $siteurl, 'https' ) );
+
+               $filename = ( DIR_TESTDATA . '/images/test-image.jpg' );
+               $contents = file_get_contents( $filename );
+
+               $upload = wp_upload_bits( basename( $filename ), null, $contents );
+               $this->assertTrue( empty( $upload['error'] ) );
+
+               // Set attachment ID
+               $attachment_id = $this->_make_attachment( $upload );
+
+               // Save server data for cleanup.
+               $is_ssl = is_ssl();
+               $http_host = $_SERVER['HTTP_HOST'];
+
+               $_SERVER['HTTPS'] = 'on';
+
+               // Set server host to something random.
+               $_SERVER['HTTP_HOST'] = 'some.otherhostname.com';
+
+               $url = wp_get_attachment_url( $attachment_id );
+               $this->assertSame( set_url_scheme( $url, 'http' ), $url );
+
+               // Cleanup.
+               $_SERVER['HTTPS'] = $is_ssl ? 'on' : 'off';
+               $_SERVER['HTTP_HOST'] = $http_host;
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre>
</div>
</div>

</body>
</html>