   uint32_t now = millis();
   Serial.printf("%07u.%03u: %s\n", now / 1000, now % 1000, str.c_str());
 #endif  // DEBUG
+}
+
+String timeSince(uint32_t const start) {
+  if (start == 0)
+    return "Never";
+  uint32_t diff = 0;
+  uint32_t now = millis();
+  if (start < now)
+    diff = now - start;
+  else
+    diff = UINT32_MAX - start + now;
+  diff /= 1000;  // Convert to seconds.
+  if (diff == 0)  return "Now";
+
+  // Note: millis() can only count up to 45 days, so uint8_t is safe.
+  uint8_t days = diff / (60 * 60 * 24);
+  uint8_t hours = (diff / (60 * 60)) % 24;
+  uint8_t minutes = (diff / 60) % 60;
+  uint8_t seconds = diff % 60;
+
+  String result = "";
+  if (days)
+    result += String(days) + " day";
+  if (days > 1)  result += "s";
+  if (hours)
+    result += " " + String(hours) + " hour";
+  if (hours > 1)  result += "s";
+  if (minutes)
+    result += " " + String(minutes) + " minute";
+  if (minutes > 1)  result += "s";
+  if (seconds)
+    result += " " + String(seconds) + " second";
+  if (seconds > 1)  result += "s";
+  result.trim();
+  return result + " ago";
+}
+
+// Quick and dirty check for any unsafe chars in a string
+// that may cause HTML shenanigans. e.g. An XSS.
+bool hasUnsafeHTMLChars(String input) {
+  static char unsafe[] = "';!-\"<>=&{}()";
+  for (uint8_t i = 0; unsafe[i]; i++)
+    if (input.indexOf(unsafe[i]) != -1) return true;
+  return false;
 }
 
 // Root web page with example usage etc.

    "<body>"
    "<center><h1>ESP8266 IR MQTT Server</h1></center>"
    "<br><hr>"
    "<h3>Connection details</h3>"
    "<p>IP address: " + WiFi.localIP().toString() + "</p>"
#ifdef MQTT_ENABLE
    "<p>MQTT server: " MQTT_SERVER ":" + String(MQTT_PORT) + " ("+
    (mqtt_client.connected() ? "Connected" : "Disconnected") + ")<br>"
    "Command topic: " MQTTcommand "<br>"
    "Acknowledgements topic: " MQTTack "</p>"
#endif  // MQTT_ENABLE
    "<br><hr>"
    "<h3>Hardcoded examples</h3>"

    case TROTEC:  // 28
    case TOSHIBA_AC:  // 32
    case FUJITSU_AC:  // 33
    case HAIER_AC:  // 38
      parseStringAndSendAirCon(ir_type, code_str);
      break;
#if SEND_DENON
