├── .gitignore ├── robots.txt ├── assets ├── css │ ├── noscript.css │ └── style.css └── js │ └── script.js ├── Makefile ├── README ├── COPYING ├── helpers ├── whatsmychaincert.xslt └── mkconfigguide └── index.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /index.html 2 | /index.htmlgz 3 | /configguide.xml 4 | /config.mk 5 | -------------------------------------------------------------------------------- /robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: LinkChecker 2 | Disallow: 3 | 4 | User-agent: panscient.com 5 | Disallow: / 6 | 7 | User-agent: * 8 | Disallow: /generate 9 | Disallow: /test 10 | -------------------------------------------------------------------------------- /assets/css/noscript.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Opsmate, Inc. 3 | * 4 | * See COPYING file for license information. 5 | */ 6 | #configguide p.select { display: none; } 7 | #configguide h4 { display: block; margin: 0 0 0.5em 0; } 8 | #configguide .configguide_snippet { display: block; margin-bottom: 1em; } 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2014 Opsmate, Inc. 3 | # 4 | # See COPYING file for license information. 5 | # 6 | 7 | INSTALL = install 8 | INSTALL_WEBASSETS = cp -dR 9 | ENDPOINT = https://whatsmychaincert.com 10 | FILES = index.html index.htmlgz robots.txt 11 | TLSCONFIGGUIDE = ../tlsconfigguide 12 | 13 | -include config.mk 14 | 15 | export TLSCONFIGGUIDE 16 | 17 | all: index.html index.htmlgz 18 | 19 | index.html: index.xml helpers/whatsmychaincert.xslt configguide.xml 20 | xsltproc --stringparam endpoint "$(ENDPOINT)" helpers/whatsmychaincert.xslt index.xml > $@ 21 | 22 | index.htmlgz: index.html 23 | gzip -n9 < $< > $@ 24 | 25 | configguide.xml: helpers/mkconfigguide $(TLSCONFIGGUIDE)/templates.index 26 | helpers/mkconfigguide > $@ 27 | 28 | clean: 29 | rm -f index.html configguide.xml 30 | 31 | ifneq ($(DESTDIR),) 32 | install: $(FILES) 33 | $(INSTALL) -m 755 -d $(DESTDIR) 34 | $(INSTALL) -m 644 $(FILES) $(DESTDIR)/ 35 | $(INSTALL_WEBASSETS) assets/ $(DESTDIR)/assets 36 | else 37 | install: 38 | @echo "Please set DESTDIR variable to use 'make install'" 39 | @false 40 | endif 41 | 42 | .PHONY: all clean install 43 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This repository contains the source code for the frontend of 2 | , a website that helps you test and 3 | configure SSL chain certificates. whatsmychaincert.com tests if your 4 | server is serving the correct certificate chain, tells you what chain 5 | you should be serving, and helps you configure your server to serve it. 6 | Head on over to the site for more information. 7 | 8 | index.html is compiled from index.xml by running `make`. 9 | 10 | To compile index.html, you need: 11 | 12 | * GNU Make 13 | * xsltproc 14 | * AT&T Kornshell 15 | * SSLMate's tlsconfigguide repository 16 | 17 | By default, the tlsconfigguide repository is expected to be found 18 | in ../tlsconfigguide, but this can be overridden by setting the 19 | TLSCONFIGGUIDE variable in config.mk or on the make command line. 20 | 21 | whatsmychaincert.com was developed by SSLMate , a 22 | service for buying and managing SSL certs from the command line. SSLMate 23 | saves you time and effort by automating away the error-prone tedium of 24 | CSR generation, certificate chain assembly, and renewals. 25 | 26 | Copyright (C) 2014 Opsmate, Inc. 27 | Licensed under the MIT/X11 license (see COPYING for details) 28 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (C) 2014 Opsmate, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a 4 | copy of this software and associated documentation files (the "Software"), 5 | to deal in the Software without restriction, including without limitation 6 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the 8 | Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included 11 | in all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 17 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 18 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 19 | OTHER DEALINGS IN THE SOFTWARE. 20 | 21 | Except as contained in this notice, the name(s) of the above copyright 22 | holders shall not be used in advertising or otherwise to promote the 23 | sale, use or other dealings in this Software without prior written 24 | authorization. 25 | -------------------------------------------------------------------------------- /helpers/whatsmychaincert.xslt: -------------------------------------------------------------------------------- 1 | 6 | 12 | 13 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /helpers/mkconfigguide: -------------------------------------------------------------------------------- 1 | #!/bin/ksh 2 | 3 | # 4 | # Copyright (C) 2014 Opsmate, Inc. 5 | # 6 | # See COPYING file for license information. 7 | # 8 | 9 | function escape_xml { 10 | print -r -- "$1" | sed \ 11 | -e "s/&/\&/g" \ 12 | -e "s//\>/g" \ 14 | -e "s/\"/\"/g" \ 15 | -e "s/'/\'/g" 16 | } 17 | 18 | template_dir=${TLSCONFIGGUIDE:?}/templates 19 | software=$(sed -e '/^#/ d' -e '/^$/ d' < ${TLSCONFIGGUIDE:?}/templates.index) 20 | 21 | print "" 22 | print "
" 23 | print "\t

" 24 | print "\t\tChoose your software:" 25 | print "\t\t" 31 | print "\t\tContribute config templates" 32 | print "\t

" 33 | 34 | once=0 35 | print -r -- "$software" | while read name title 36 | do 37 | if (( once )) 38 | then 39 | selected_class= 40 | else 41 | selected_class=" configguide_snippet_selected" 42 | once=1 43 | fi 44 | 45 | print "\t
" 46 | print "\t\t

${title}

" 47 | print "\t\t
" 48 | sed -e 's|__KEY_PATH__|/path/to/example.com.key|g' \ 49 | -e 's|__CERT_PATH__|/path/to/example.com.crt|g' \ 50 | -e 's|__CHAIN_PATH__|/path/to/example.com.chain.crt|g' \ 51 | -e 's|__CHAINED_PATH__|/path/to/example.com.chained.crt|g' \ 52 | -e 's|__COMBINED_PATH__|/path/to/example.com.combined.pem|g' \ 53 | < "$template_dir/${name}" | while IFS=$'\n' read -r line 54 | do 55 | print "\t\t\t

$(escape_xml "$line")

" 56 | done 57 | print "\t\t
" 58 | print "\t
" 59 | 60 | done 61 | 62 | print "
" 63 | print "" 64 | -------------------------------------------------------------------------------- /assets/css/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Opsmate, Inc. 3 | * 4 | * See COPYING file for license information. 5 | */ 6 | html, body { color: black; background: white; font-family: sans-serif; } 7 | a:link { color: #00A; } 8 | a:visited { color: #639; } 9 | a[href]:hover, a[href]:focus { color: #06F; } 10 | a:active { color: #669; } 11 | a, a:link, a:hover, a:visited, a:active { text-decoration: underline; } 12 | #masthead { text-align: center; margin: 2em 0; } 13 | #masthead p.by { margin: 0.5em 0 0 0; } 14 | h1 { font-size: 250%; text-align: center; margin: 0; } 15 | h2 { margin: 1em 0 0.4em 0; } 16 | form { margin: 0; } 17 | .nobr { white-space: nowrap; } 18 | #root { max-width: 800px; margin: 0 auto; font-size: 120%; text-align: justify; } 19 | #root p { line-height: 160%; } 20 | .box { background: #eee; border-radius: 8px; padding: 0.5em; } 21 | .box p { margin: 0; } 22 | .box .instructions.minor { margin-top: 0.5em; } 23 | #root .box p { line-height: 130%; } 24 | .test input { font-size: 160%; } 25 | .test input[type=text] { background: #FCFCFC; } 26 | .test input[disabled] { cursor: wait; } 27 | .generate p.buttons { margin-top: 0.7em; } 28 | .generate input { font-size: 130%; } 29 | .generate textarea { width: 99%; background: #FCFCFC; } 30 | .generate .by_hostname input[type=text] { font-size: 160%; background: #FCFCFC; } 31 | /* 32 | .generate .by_hostname input { font-size: 160%; } 33 | .generate .by_hostname input[type=text] { background: #FCFCFC; } 34 | */ 35 | .generate .box.by_hostname { margin-top: 1em; } 36 | .box .instructions.major { margin-bottom: 0.5em; } 37 | .box .instructions.minor { font-size: 70%; } 38 | code.block { display: block; background: #eee; border-radius: 4px; padding: 0.2em 0.5em; font-size: 90%; text-align: left; } 39 | .config select { font-size: 110%; } 40 | 41 | #configguide h4 { display: none; } 42 | #configguide p.select { margin-bottom: 0.5em; } 43 | #configguide .configguide_snippet { display: none; } 44 | #configguide .configguide_snippet_selected { display: block; } 45 | 46 | .terminal { font-family: monospace; overflow: auto; white-space: nowrap; background: white; margin: 0; text-align: left; background: #FCFCFC; border: solid #DDD 1px; border-radius: 5px; padding: 3px 5px; color: black; } 47 | .terminal p { margin: 0; white-space: pre; } 48 | 49 | #test_results { font-size: 140%; line-height: 140%; text-align: left; } 50 | #test_results p { margin: 0 0 0.5em 0; } 51 | #test_results p.test_error { color: #F40; } 52 | #test_results .result_trusted { color: #060; font-weight: bold; } 53 | #test_results .result_trusted_but_expired_chain { color: #F40; font-weight: bold; } 54 | #test_results .result_untrusted { color: #900; font-weight: bold; } 55 | #test_results .result_expired { color: #F40; font-weight: bold; } 56 | #test_results .result_self_signed { color: #F40; font-weight: bold; } 57 | #test_results .result_error { color: #F40; } 58 | 59 | #footer p { margin: 0.5em; } 60 | #footer { margin-top: 2em; text-align: center; color: #777; font-size: 90%; } 61 | #footer a { color: inherit; margin-left: 0.3em; } 62 | #footer a:hover { color: #222; } 63 | -------------------------------------------------------------------------------- /index.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | What's My Chain Cert? 10 | 11 | 12 | 15 | 17 | 20 | 21 | 22 |
23 |
24 |

What's My Chain Cert?

25 |

By SSLMate

26 |
27 | 28 |

29 | Did you know that when you install an SSL certificate, you have to install 30 | not only your site's certificate, but also one or more intermediate (a.k.a. chain) 31 | certificates? Failure to install the correct chain can cause certificate 32 | errors in browsers, driving visitors away from your site. To complicate matters, 33 | some browsers cache intermediate certificates, or download missing intermediates 34 | on-demand, meaning that an improperly-configured chain could work in some browsers 35 | but not others, making this an annoying problem to debug. 36 |

37 |

38 | This site tests if your server is serving the correct certificate chain, 39 | tells you what chain you should be serving, and helps you configure your 40 | server to serve it. 41 |

42 | 43 |
44 |

Test Your Server

45 |
46 |
47 |
48 |
49 | 50 | 51 |
52 |

Checks port 443 (HTTPS) by default. For a different port, specify it with the hostname like: example.com:993

53 |
54 |
55 |
56 |

Generate the Correct Chain

57 |

58 | The generated chain will include your server's leaf certificate, followed 59 | by every required intermediate certificate, optionally followed by the root 60 | certificate. 61 |

62 |
63 |

64 | Paste your certificate in the box below to generate the correct chain for it, 65 | based on the metadata embedded in the certificate. 66 |

67 |
68 | 69 |

70 |

71 | 72 | 76 |

77 |
78 |
79 |
80 |

81 | Or, enter the hostname of a server to generate the correct chain for its certificate: 82 |

83 |
84 | 85 |

86 |

87 | 88 | 92 |

93 |
94 |
95 |
96 |
97 |

Include the Root Certificate?

98 | 99 |

100 | You do not need to include the root certificate in 101 | the certificate chain that you serve, since clients already have 102 | the root certificate in their trust stores. Including the root is 103 | inefficient since it increases the size of the SSL handshake. 104 |

105 |

106 | A separate chain that includes the root certificate is sometimes 107 | used for other purposes, such as 108 | OCSP stapling. 109 | Such advanced configuration is beyond the scope of this guide, 110 | although the generator will generate such chains if you 111 | check the "Include Root Certificate" box. 112 |

113 |
114 |
115 |

Configure Your Server

116 | 117 |

118 | Note: some software requires you to put your site's certificate chain 119 | (e.g. example.com.chained.crt) and your private key 120 | (e.g. example.com.key) in separate files, while other 121 | software requires you to put them in the same file. 122 |

123 | 124 |

125 | You can generate the combined file (example.com.combined.pem) with a command such as: 126 |

127 |

128 | cat example.com.key example.com.chained.crt > example.com.combined.pem 129 |

130 | 131 |
132 | 133 |
134 | 135 |

136 | Don't forget to restart your server software after changing its configuration! 137 |

138 |
139 |
140 |

A Better Way to Get SSL Certificates

141 | 142 |

143 | SSLMate lets you get SSL certs from 144 | the command line. SSLMate saves you time and effort by automating away the error-prone 145 | tedium of CSR generation, certificate chain assembly, and renewals. 146 |

147 |
148 | 156 |
157 | 158 | 159 | -------------------------------------------------------------------------------- /assets/js/script.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Opsmate, Inc. 3 | * 4 | * See COPYING file for license information. 5 | */ 6 | var whatsmychaincert_endpoint; 7 | var current_test_host = null; 8 | 9 | var create_element; 10 | 11 | if (typeof(document.createElementNS) == "function" && document.documentElement.namespaceURI) 12 | create_element = function (name) { return document.createElementNS(document.documentElement.namespaceURI, name); }; 13 | else 14 | create_element = function (name) { return document.createElement(name); } 15 | 16 | var has_push_state = ("pushState" in history) && typeof(history.pushState) == "function" && 17 | ("replaceState" in history) && typeof(history.replaceState) == "function"; 18 | 19 | function add_class (element, className) 20 | { 21 | if (!has_class(element, className)) { 22 | if (element.className) element.className += " " + className; 23 | else element.className = className; 24 | } 25 | } 26 | 27 | function remove_class (element, className) 28 | { 29 | var regexp = new RegExp("(^|\\s)" + className + "(\\s|$)"); 30 | element.className = element.className.replace(regexp, "$2"); 31 | } 32 | 33 | function has_class (element, className) 34 | { 35 | var regexp = new RegExp("(^|\\s)" + className + "(\\s|$)"); 36 | return regexp.test(element.className); 37 | } 38 | 39 | function canonical_url (test_host) 40 | { 41 | var url = location.protocol + "//" + location.host + location.pathname; 42 | if (test_host) { 43 | url += "?" + encodeURI(test_host); 44 | } 45 | return url; 46 | } 47 | 48 | function add_testing_message (host) 49 | { 50 | var p = create_element("p"); 51 | p.appendChild(document.createTextNode("Testing " + host + "... ")); 52 | 53 | var cancel_link = create_element("a"); 54 | cancel_link.href = "javascript:void(0);"; 55 | cancel_link.onclick = function() { cancel_test(); }; 56 | cancel_link.appendChild(document.createTextNode("Cancel")); 57 | p.appendChild(cancel_link); 58 | 59 | clear_test_results(); 60 | document.getElementById("test_results").appendChild(p); 61 | } 62 | 63 | function add_test_result (host, ip_address, type, text) 64 | { 65 | var results = document.getElementById("test_results"); 66 | 67 | var result = create_element("p"); 68 | var host_span = create_element("span"); 69 | host_span.className = "result_host"; 70 | host_span.appendChild(document.createTextNode(host)); 71 | result.appendChild(host_span); 72 | if (ip_address) { 73 | result.appendChild(document.createTextNode(" (")); 74 | var ip_address_span = create_element("span"); 75 | ip_address_span.className = "result_ip_address"; 76 | ip_address_span.appendChild(document.createTextNode(ip_address)); 77 | result.appendChild(ip_address_span); 78 | result.appendChild(document.createTextNode(")")); 79 | } 80 | if (type == "trusted") { 81 | result.appendChild(document.createTextNode(" has the ")); 82 | var trusted_span = create_element("span"); 83 | trusted_span.className = "result_trusted"; 84 | trusted_span.appendChild(document.createTextNode("correct")); 85 | result.appendChild(trusted_span); 86 | result.appendChild(document.createTextNode(" chain.")); 87 | } else if (type == "trusted_but_expired_chain") { 88 | result.appendChild(document.createTextNode(" has a trusted chain containing an ")); 89 | var span = create_element("span"); 90 | span.className = "result_trusted_but_expired_chain"; 91 | span.appendChild(document.createTextNode("expired certificate")); 92 | result.appendChild(span); 93 | result.appendChild(document.createTextNode(". This chain will work with modern web browsers (they will use a chain to a different trusted root) but ")); 94 | var a = create_element("a"); 95 | a.href = "https://www.agwa.name/blog/post/fixing_the_addtrust_root_expiration"; 96 | a.appendChild(document.createTextNode("may fail with older clients")); 97 | result.appendChild(a); 98 | result.appendChild(document.createTextNode(", notably OpenSSL 1.0.x. ")); 99 | a = create_element("a"); 100 | a.href = whatsmychaincert_endpoint + "/generate?include_leaf=1;host=" + encodeURIComponent(host); 101 | if (ip_address) { 102 | a.href += ";ip_address=" + encodeURIComponent(ip_address); 103 | } 104 | a.appendChild(document.createTextNode("Download a chain without the expired certificate")); 105 | result.appendChild(a); 106 | } else if (type == "expired") { 107 | result.appendChild(document.createTextNode(" is ")); 108 | var expired_span = create_element("span"); 109 | expired_span.className = "result_expired"; 110 | expired_span.appendChild(document.createTextNode("expired")); 111 | result.appendChild(expired_span); 112 | result.appendChild(document.createTextNode(". It won't be trusted by clients regardless of its chain.")); 113 | } else if (type == "self_signed") { 114 | result.appendChild(document.createTextNode(" is ")); 115 | var self_signed_span = create_element("span"); 116 | self_signed_span.className = "result_self_signed"; 117 | self_signed_span.appendChild(document.createTextNode("self-signed")); 118 | result.appendChild(self_signed_span); 119 | result.appendChild(document.createTextNode(". It doesn't have a chain certificate and will never be trusted by clients.")); 120 | } else if (type == "untrusted") { 121 | result.appendChild(document.createTextNode(" is ")); 122 | var untrusted_span = create_element("span"); 123 | untrusted_span.className = "result_untrusted"; 124 | untrusted_span.appendChild(document.createTextNode("misconfigured")); 125 | result.appendChild(untrusted_span); 126 | result.appendChild(document.createTextNode(". ")); 127 | var a = create_element("a"); 128 | a.href = whatsmychaincert_endpoint + "/generate?include_leaf=1;host=" + encodeURIComponent(host); 129 | if (ip_address) { 130 | a.href += ";ip_address=" + encodeURIComponent(ip_address); 131 | } 132 | a.appendChild(document.createTextNode("This")); 133 | result.appendChild(a); 134 | result.appendChild(document.createTextNode(" is the chain it should be using.")); 135 | } else { 136 | result.appendChild(document.createTextNode(": ")); 137 | var error_span = create_element("span"); 138 | error_span.className = "result_error"; 139 | error_span.appendChild(document.createTextNode(text)); 140 | result.appendChild(error_span); 141 | if (type == "handshake_error") { 142 | result.appendChild(document.createTextNode(" ")); 143 | var ssllabs_link = create_element("a"); 144 | ssllabs_link.href = "https://www.ssllabs.com/ssltest/analyze.html?d=" + encodeURIComponent(host); 145 | if (ip_address) { 146 | ssllabs_link.href += "&s=" + encodeURIComponent(ip_address); 147 | } 148 | ssllabs_link.href += "&hideResults=on"; 149 | ssllabs_link.appendChild(document.createTextNode("SSL Labs")); 150 | result.appendChild(ssllabs_link); 151 | result.appendChild(document.createTextNode(" might be able to tell you what went wrong")); 152 | } 153 | } 154 | 155 | results.appendChild(result); 156 | add_class(results, "has_results"); 157 | } 158 | 159 | function clear_test_results () 160 | { 161 | var results = document.getElementById("test_results"); 162 | while (results.hasChildNodes()) { 163 | results.removeChild(results.firstChild); 164 | } 165 | remove_class(results, "has_results"); 166 | } 167 | 168 | function get_result_type (result) 169 | { 170 | if (result.trusted) { 171 | if (result.has_expired_chain) { 172 | return "trusted_but_expired_chain"; 173 | } 174 | return "trusted"; 175 | } else if (result.trust_error == "self_signed") { 176 | return "self_signed"; 177 | } else if (result.trust_error == "expired") { 178 | return "expired"; 179 | } else { 180 | return "untrusted"; 181 | } 182 | } 183 | 184 | function get_singular_result (results) 185 | { 186 | for (var i = 1; i < results.length; ++i) { 187 | if (results[i] != results[i - 1]) { 188 | return null; 189 | } 190 | } 191 | return results.length ? results[0] : null; 192 | } 193 | 194 | function handle_test_results (host, result) 195 | { 196 | var results = []; 197 | var has_errors = false; 198 | for (var i = 0; i < result.length; ++i) { 199 | if ("trusted" in result[i]) { 200 | results.push(get_result_type(result[i])); 201 | } else { 202 | has_errors = true; 203 | } 204 | } 205 | clear_test_results(); 206 | var singular_result = get_singular_result(results); 207 | if (!has_errors && singular_result) { 208 | add_test_result(host, null, singular_result); 209 | } else { 210 | for (var i = 0; i < result.length; ++i) { 211 | if ("trusted" in result[i]) { 212 | add_test_result(host, result[i].ip_address, get_result_type(result[i])); 213 | } else { 214 | add_test_result(host, result[i].ip_address, result[i].error_type + "_error", result[i].error); 215 | } 216 | } 217 | } 218 | } 219 | 220 | function handle_test_error (message) 221 | { 222 | clear_test_results(); 223 | 224 | var results = document.getElementById("test_results"); 225 | var result = create_element("p"); 226 | result.className = "test_error"; 227 | result.appendChild(document.createTextNode(message)); 228 | results.appendChild(result); 229 | add_class(results, "has_results"); 230 | } 231 | 232 | function test_form_submit (form) 233 | { 234 | var host = form.host.value; 235 | if ("trim" in String.prototype) { // not supported before IE9 236 | host = host.trim(); 237 | } 238 | var re = /^https?:\/\/([^\/]*)/; 239 | var match = re.exec(host); 240 | if (match) { 241 | host = match[1]; 242 | } 243 | if (host == "") { 244 | return false; 245 | } 246 | if (has_push_state) { 247 | history.pushState(host, null, canonical_url(host)); 248 | } 249 | do_test(host, form); 250 | return false; 251 | } 252 | 253 | function do_test (host, form) 254 | { 255 | var uri = whatsmychaincert_endpoint + "/test?host=" + encodeURIComponent(host); 256 | var xhr = new XMLHttpRequest(); 257 | xhr.onreadystatechange = function () { 258 | if (xhr.readyState == 4) { 259 | if (host != current_test_host) { 260 | return; 261 | } 262 | current_test_host = null; 263 | form['host'].disabled = false; 264 | form['submit_btn'].disabled = false; 265 | if (xhr.status != 200) { 266 | handle_test_error(xhr.responseText); 267 | } else if (xhr.getResponseHeader("Content-Type") != "application/json") { 268 | handle_test_error("Received an unexpected response from the server."); 269 | } else { 270 | handle_test_results(host, eval("(" + xhr.responseText + ")")); 271 | } 272 | } 273 | }; 274 | current_test_host = host; 275 | xhr.open("GET", uri); 276 | xhr.send(); 277 | add_testing_message(host); 278 | form['host'].disabled = true; 279 | form['submit_btn'].disabled = true; 280 | } 281 | 282 | function cancel_test () 283 | { 284 | var test_form = document.getElementById('test_form'); 285 | test_form['host'].disabled = false; 286 | test_form['submit_btn'].disabled = false; 287 | clear_test_results(); 288 | current_test_host = null; 289 | } 290 | 291 | function select_configguide_snippet (snippet) 292 | { 293 | var cg = document.getElementById('configguide'); 294 | var nd = cg.firstChild; 295 | for (;nd; nd = nd.nextSibling) { 296 | if (nd.nodeName.toLowerCase() == "div" && has_class(nd, "configguide_snippet")) { 297 | if (has_class(nd, "configguide_snippet_" + snippet)) { 298 | add_class(nd, "configguide_snippet_selected"); 299 | } else { 300 | remove_class(nd, "configguide_snippet_selected"); 301 | } 302 | } 303 | } 304 | } 305 | 306 | function restore_state (test_host) 307 | { 308 | var test_form = document.getElementById('test_form'); 309 | if (test_host) { 310 | test_form.host.value = test_host; 311 | do_test(test_host, test_form); 312 | } else { 313 | test_form.reset(); 314 | clear_test_results(); 315 | } 316 | if (has_push_state) { 317 | history.replaceState(test_host, null, canonical_url(test_host)); 318 | } 319 | } 320 | 321 | window.onload = function () 322 | { 323 | var cg = document.getElementById('configguide'); 324 | select_configguide_snippet(cg.snippet.value); 325 | restore_state(location.search ? decodeURI(location.search.substr(1)) : null); 326 | } 327 | window.onpopstate = function (ev) 328 | { 329 | restore_state(ev.state); 330 | } 331 | --------------------------------------------------------------------------------