Code

internationalization fixes
[nagiosplug.git] / plugins / check_http.c
index 3ec9e4992211ce2fe3a6b7368f4533dd3b691a77..6e76706277d702712a92bfa3e34656b9fcb29bf5 100644 (file)
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
+ $Id$
 ******************************************************************************/
+/* splint -I. -I../../plugins -I../../lib/ -I/usr/kerberos/include/ ../../plugins/check_http.c */
 
 const char *progname = "check_http";
 const char *revision = "$Revision$";
-const char *copyright = "1999-2001";
+const char *copyright = "1999-2004";
 const char *email = "nagiosplug-devel@lists.sourceforge.net";
 
 #include "common.h"
 #include "netutils.h"
 #include "utils.h"
 
+#define INPUT_DELIMITER ";"
+
 #define HTTP_EXPECT "HTTP/1."
 enum {
        MAX_IPV4_HOSTLENGTH = 255,
@@ -62,6 +67,8 @@ X509 *server_cert;
 int connect_SSL (void);
 int check_certificate (X509 **);
 #endif
+int no_body = FALSE;
+int maximum_age = -1;
 
 #ifdef HAVE_REGEX_H
 enum {
@@ -90,6 +97,7 @@ char server_type[6] = "http";
 char *server_address;
 char *host_name;
 char *server_url;
+char *user_agent;
 int server_url_length;
 int server_expect_yn = 0;
 char server_expect[MAX_INPUT_BUFFER] = HTTP_EXPECT;
@@ -100,6 +108,7 @@ double critical_time = 0;
 int check_critical_time = FALSE;
 char user_auth[MAX_INPUT_BUFFER] = "";
 int display_html = FALSE;
+char *http_opt_headers;
 int onredirect = STATE_OK;
 int use_ssl = FALSE;
 int verbose = FALSE;
@@ -109,14 +118,17 @@ int redir_depth = 0;
 int max_depth = 15;
 char *http_method;
 char *http_post_data;
+char *http_content_type;
 char buffer[MAX_INPUT_BUFFER];
 
 int process_arguments (int, char **);
 static char *base64 (const char *bin, size_t len);
 int check_http (void);
-int redir (char *pos, char *status_line);
+void redir (char *pos, char *status_line);
 int server_type_check(const char *type);
 int server_port_check(int ssl_flag);
+char *perfd_time (double microsec);
+char *perfd_size (int page_len);
 int my_recv (void);
 int my_close (void);
 void print_help (void);
@@ -130,9 +142,11 @@ main (int argc, char **argv)
        /* Set default URL. Must be malloced for subsequent realloc if --onredirect=follow */
        server_url = strdup(HTTP_URL);
        server_url_length = strlen(server_url);
+       asprintf (&user_agent, "User-Agent: check_http/%s (nagios-plugins %s)",
+                 clean_revstring (revision), VERSION);
 
        if (process_arguments (argc, argv) == ERROR)
-               usage (_("check_http: could not parse arguments\n"));
+               usage4 (_("Could not parse arguments"));
 
        if (strstr (timestamp, ":")) {
                if (strstr (server_url, "?"))
@@ -142,8 +156,9 @@ main (int argc, char **argv)
        }
 
        if (display_html == TRUE)
-               printf ("<A HREF=\"http://%s:%d%s\" target=\"_blank\">",
-                       host_name, server_port, server_url);
+               printf ("<A HREF=\"%s://%s:%d%s\" target=\"_blank\">", 
+                       use_ssl ? "https" : "http", host_name,
+                       server_port, server_url);
 
        /* initialize alarm signal handling, set socket timeout, start timer */
        (void) signal (SIGALRM, socket_timeout_alarm_handler);
@@ -159,7 +174,7 @@ main (int argc, char **argv)
                        X509_free (server_cert);
                }
                else {
-                       printf (_("ERROR: Cannot retrieve server certificate.\n"));
+                       printf (_("CRITICAL - Cannot retrieve server certificate.\n"));
                        result = STATE_CRITICAL;
                }
                SSL_shutdown (ssl);
@@ -175,7 +190,7 @@ main (int argc, char **argv)
 #endif
        return result;
 }
-\f
+
 
 
 /* process command-line arguments */
@@ -194,6 +209,7 @@ process_arguments (int argc, char **argv)
                {"verbose", no_argument, 0, 'v'},
                {"post", required_argument, 0, 'P'},
                {"IP-address", required_argument, 0, 'I'},
+               {"url", required_argument, 0, 'u'},
                {"string", required_argument, 0, 's'},
                {"regex", required_argument, 0, 'r'},
                {"ereg", required_argument, 0, 'r'},
@@ -201,6 +217,11 @@ process_arguments (int argc, char **argv)
                {"linespan", no_argument, 0, 'l'},
                {"onredirect", required_argument, 0, 'f'},
                {"certificate", required_argument, 0, 'C'},
+               {"useragent", required_argument, 0, 'A'},
+               {"header", required_argument, 0, 'k'},
+               {"no-body", no_argument, 0, 'N'},
+               {"max-age", required_argument, 0, 'M'},
+               {"content-type", required_argument, 0, 'T'},
                {"min", required_argument, 0, 'm'},
                {"use-ipv4", no_argument, 0, '4'},
                {"use-ipv6", no_argument, 0, '6'},
@@ -224,13 +245,13 @@ process_arguments (int argc, char **argv)
        }
 
        while (1) {
-               c = getopt_long (argc, argv, "Vvh46t:c:w:H:P:I:a:e:p:s:R:r:u:f:C:nlLSm:", longopts, &option);
+               c = getopt_long (argc, argv, "Vvh46t:c:w:A:k:H:P:T:I:a:e:p:s:R:r:u:f:C:nlLSm:M:N", longopts, &option);
                if (c == -1 || c == EOF)
                        break;
 
                switch (c) {
                case '?': /* usage */
-                       usage3 (_("unknown argument"), optopt);
+                       usage2 (_("Unknown argument"), optarg);
                        break;
                case 'h': /* help */
                        print_help ();
@@ -242,26 +263,32 @@ process_arguments (int argc, char **argv)
                        break;
                case 't': /* timeout period */
                        if (!is_intnonneg (optarg))
-                               usage2 (_("timeout interval must be a non-negative integer"), optarg);
+                               usage2 (_("Timeout interval must be a positive integer"), optarg);
                        else
                                socket_timeout = atoi (optarg);
                        break;
                case 'c': /* critical time threshold */
-                       if (!is_intnonneg (optarg))
-                               usage2 (_("invalid critical threshold"), optarg);
+                       if (!is_nonnegative (optarg))
+                               usage2 (_("Critical threshold must be integer"), optarg);
                        else {
                                critical_time = strtod (optarg, NULL);
                                check_critical_time = TRUE;
                        }
                        break;
                case 'w': /* warning time threshold */
-                       if (!is_intnonneg (optarg))
-                               usage2 (_("invalid warning threshold"), optarg);
+                       if (!is_nonnegative (optarg))
+                               usage2 (_("Warning threshold must be integer"), optarg);
                        else {
                                warning_time = strtod (optarg, NULL);
                                check_warning_time = TRUE;
                        }
                        break;
+               case 'A': /* User Agent String */
+                       asprintf (&user_agent, "User-Agent: %s", optarg);
+                       break;
+               case 'k': /* Additional headers */
+                       asprintf (&http_opt_headers, "%s", optarg);
+                       break;
                case 'L': /* show html link */
                        display_html = TRUE;
                        break;
@@ -270,7 +297,7 @@ process_arguments (int argc, char **argv)
                        break;
                case 'S': /* use SSL */
 #ifndef HAVE_SSL
-                       usage (_("check_http: invalid option - SSL is not available\n"));
+                       usage4 (_("Invalid option - SSL is not available"));
 #endif
                        use_ssl = TRUE;
                        if (specify_port == FALSE)
@@ -279,13 +306,13 @@ process_arguments (int argc, char **argv)
                case 'C': /* Check SSL cert validity */
 #ifdef HAVE_SSL
                        if (!is_intnonneg (optarg))
-                               usage2 (_("invalid certificate expiration period"), optarg);
+                               usage2 (_("Invalid certificate expiration period"), optarg);
                        else {
                                days_till_exp = atoi (optarg);
                                check_cert = TRUE;
                        }
 #else
-                       usage (_("check_http: invalid option - SSL is not available\n"));
+                       usage4 (_("Invalid option - SSL is not available"));
 #endif
                        break;
                case 'f': /* onredirect */
@@ -305,6 +332,8 @@ process_arguments (int argc, char **argv)
                /* Note: H, I, and u must be malloc'd or will fail on redirects */
                case 'H': /* Host Name (virtual host) */
                        host_name = strdup (optarg);
+                       if (strstr (optarg, ":"))
+                               sscanf (optarg, "%*[^:]:%d", &server_port);
                        break;
                case 'I': /* Server IP-address */
                        server_address = strdup (optarg);
@@ -315,7 +344,7 @@ process_arguments (int argc, char **argv)
                        break;
                case 'p': /* Server port */
                        if (!is_intnonneg (optarg))
-                               usage2 (_("invalid port number"), optarg);
+                               usage2 (_("Invalid port number"), optarg);
                        else {
                                server_port = atoi (optarg);
                                specify_port = TRUE;
@@ -339,11 +368,14 @@ process_arguments (int argc, char **argv)
                        server_expect[MAX_INPUT_BUFFER - 1] = 0;
                        server_expect_yn = 1;
                        break;
+               case 'T': /* Content-type */
+                       asprintf (&http_content_type, "%s", optarg);
+                       break;
 #ifndef HAVE_REGEX_H
                case 'l': /* linespan */
                case 'r': /* linespan */
                case 'R': /* linespan */
-                       usage (_("check_http: call for regex which was not a compiled option\n"));
+                       usage4 (_("Call for regex which was not a compiled option"));
                        break;
 #else
                case 'l': /* linespan */
@@ -369,7 +401,7 @@ process_arguments (int argc, char **argv)
 #ifdef USE_IPV6
                        address_family = AF_INET6;
 #else
-                       usage (_("IPv6 support not available\n"));
+                       usage4 (_("IPv6 support not available"));
 #endif
                        break;
                case 'v': /* verbose */
@@ -378,6 +410,27 @@ process_arguments (int argc, char **argv)
                case 'm': /* min_page_length */
                        min_page_len = atoi (optarg);
                        break;
+               case 'N': /* no-body */
+                       no_body = TRUE;
+                       break;
+               case 'M': /* max-age */
+                  {
+                    int L = strlen(optarg);
+                    if (L && optarg[L-1] == 'm')
+                      maximum_age = atoi (optarg) * 60;
+                    else if (L && optarg[L-1] == 'h')
+                      maximum_age = atoi (optarg) * 60 * 60;
+                    else if (L && optarg[L-1] == 'd')
+                      maximum_age = atoi (optarg) * 60 * 60 * 24;
+                    else if (L && (optarg[L-1] == 's' ||
+                                   isdigit (optarg[L-1])))
+                      maximum_age = atoi (optarg);
+                    else {
+                      fprintf (stderr, "unparsable max-age: %s\n", optarg);
+                      exit (1);
+                    }
+                  }
+                  break;
                }
        }
 
@@ -391,7 +444,7 @@ process_arguments (int argc, char **argv)
 
        if (server_address == NULL) {
                if (host_name == NULL)
-                       usage (_("check_http: you must specify a server address or host name\n"));
+                       usage4 (_("You must specify a server address or host name"));
                else
                        server_address = strdup (host_name);
        }
@@ -404,7 +457,7 @@ process_arguments (int argc, char **argv)
 
        return TRUE;
 }
-\f
+
 
 
 /* written by lauri alanko */
@@ -447,19 +500,218 @@ base64 (const char *bin, size_t len)
        buf[i] = '\0';
        return buf;
 }
-\f
 
 
 
+/* Returns 1 if we're done processing the document body; 0 to keep going */
+static int
+document_headers_done (char *full_page)
+{
+       const char *body;
+
+       for (body = full_page; *body; body++) {
+               if (!strncmp (body, "\n\n", 2) || !strncmp (body, "\n\r\n", 3))
+                       break;
+       }
+
+       if (!*body)
+               return 0;  /* haven't read end of headers yet */
+
+       full_page[body - full_page] = 0;
+       return 1;
+}
+
+static time_t
+parse_time_string (const char *string)
+{
+       struct tm tm;
+       time_t t;
+       memset (&tm, 0, sizeof(tm));
+
+       /* Like this: Tue, 25 Dec 2001 02:59:03 GMT */
+
+       if (isupper (string[0])  &&  /* Tue */
+               islower (string[1])  &&
+               islower (string[2])  &&
+               ',' ==   string[3]   &&
+               ' ' ==   string[4]   &&
+               (isdigit(string[5]) || string[5] == ' ') &&   /* 25 */
+               isdigit (string[6])  &&
+               ' ' ==   string[7]   &&
+               isupper (string[8])  &&  /* Dec */
+               islower (string[9])  &&
+               islower (string[10]) &&
+               ' ' ==   string[11]  &&
+               isdigit (string[12]) &&  /* 2001 */
+               isdigit (string[13]) &&
+               isdigit (string[14]) &&
+               isdigit (string[15]) &&
+               ' ' ==   string[16]  &&
+               isdigit (string[17]) &&  /* 02: */
+               isdigit (string[18]) &&
+               ':' ==   string[19]  &&
+               isdigit (string[20]) &&  /* 59: */
+               isdigit (string[21]) &&
+               ':' ==   string[22]  &&
+               isdigit (string[23]) &&  /* 03 */
+               isdigit (string[24]) &&
+               ' ' ==   string[25]  &&
+               'G' ==   string[26]  &&  /* GMT */
+               'M' ==   string[27]  &&  /* GMT */
+               'T' ==   string[28]) {
+
+               tm.tm_sec  = 10 * (string[23]-'0') + (string[24]-'0');
+               tm.tm_min  = 10 * (string[20]-'0') + (string[21]-'0');
+               tm.tm_hour = 10 * (string[17]-'0') + (string[18]-'0');
+               tm.tm_mday = 10 * (string[5] == ' ' ? 0 : string[5]-'0') + (string[6]-'0');
+               tm.tm_mon = (!strncmp (string+8, "Jan", 3) ? 0 :
+                       !strncmp (string+8, "Feb", 3) ? 1 :
+                       !strncmp (string+8, "Mar", 3) ? 2 :
+                       !strncmp (string+8, "Apr", 3) ? 3 :
+                       !strncmp (string+8, "May", 3) ? 4 :
+                       !strncmp (string+8, "Jun", 3) ? 5 :
+                       !strncmp (string+8, "Jul", 3) ? 6 :
+                       !strncmp (string+8, "Aug", 3) ? 7 :
+                       !strncmp (string+8, "Sep", 3) ? 8 :
+                       !strncmp (string+8, "Oct", 3) ? 9 :
+                       !strncmp (string+8, "Nov", 3) ? 10 :
+                       !strncmp (string+8, "Dec", 3) ? 11 :
+                       -1);
+               tm.tm_year = ((1000 * (string[12]-'0') +
+                       100 * (string[13]-'0') +
+                       10 * (string[14]-'0') +
+                       (string[15]-'0'))
+                       - 1900);
+
+               tm.tm_isdst = 0;  /* GMT is never in DST, right? */
+
+               if (tm.tm_mon < 0 || tm.tm_mday < 1 || tm.tm_mday > 31)
+                       return 0;
+
+               /* 
+               This is actually wrong: we need to subtract the local timezone
+               offset from GMT from this value.  But, that's ok in this usage,
+               because we only comparing these two GMT dates against each other,
+               so it doesn't matter what time zone we parse them in.
+               */
+
+               t = mktime (&tm);
+               if (t == (time_t) -1) t = 0;
+
+               if (verbose) {
+                       const char *s = string;
+                       while (*s && *s != '\r' && *s != '\n')
+                       fputc (*s++, stdout);
+                       printf (" ==> %lu\n", (unsigned long) t);
+               }
+
+               return t;
+
+       } else {
+               return 0;
+       }
+}
+
+
+
+static void
+check_document_dates (const char *headers)
+{
+       const char *s;
+       char *server_date = 0;
+       char *document_date = 0;
+
+       s = headers;
+       while (*s) {
+               const char *field = s;
+               const char *value = 0;
+
+               /* Find the end of the header field */
+               while (*s && !isspace(*s) && *s != ':')
+                       s++;
+
+               /* Remember the header value, if any. */
+               if (*s == ':')
+                       value = ++s;
+
+               /* Skip to the end of the header, including continuation lines. */
+               while (*s && !(*s == '\n' && (s[1] != ' ' && s[1] != '\t')))
+                       s++;
+               s++;
+
+               /* Process this header. */
+               if (value && value > field+2) {
+                       char *ff = (char *) malloc (value-field);
+                       char *ss = ff;
+                       while (field < value-1)
+                               *ss++ = tolower(*field++);
+                       *ss++ = 0;
+
+                       if (!strcmp (ff, "date") || !strcmp (ff, "last-modified")) {
+                               const char *e;
+                               while (*value && isspace (*value))
+                                       value++;
+                               for (e = value; *e && *e != '\r' && *e != '\n'; e++)
+                                       ;
+                               ss = (char *) malloc (e - value + 1);
+                               strncpy (ss, value, e - value);
+                               ss[e - value] = 0;
+                               if (!strcmp (ff, "date")) {
+                                       if (server_date) free (server_date);
+                                       server_date = ss;
+                               } else {
+                                       if (document_date) free (document_date);
+                                       document_date = ss;
+                               }
+                       }
+                       free (ff);
+               }
+       }
+
+       /* Done parsing the body.  Now check the dates we (hopefully) parsed.  */
+       if (!server_date || !*server_date) {
+               die (STATE_UNKNOWN, _("Server date unknown\n"));
+       } else if (!document_date || !*document_date) {
+               die (STATE_CRITICAL, _("Document modification date unknown\n"));
+       } else {
+               time_t srv_data = parse_time_string (server_date);
+               time_t doc_data = parse_time_string (document_date);
+
+               if (srv_data <= 0) {
+                       die (STATE_CRITICAL, _("CRITICAL - Server date \"%100s\" unparsable"), server_date);
+               } else if (doc_data <= 0) {
+                       die (STATE_CRITICAL, _("CRITICAL - Document date \"%100s\" unparsable"), document_date);
+               } else if (doc_data > srv_data + 30) {
+                       die (STATE_CRITICAL, _("CRITICAL - Document is %d seconds in the future\n"), (int)doc_data - (int)srv_data);
+               } else if (doc_data < srv_data - maximum_age) {
+               int n = (srv_data - doc_data);
+               if (n > (60 * 60 * 24 * 2))
+                       die (STATE_CRITICAL,
+                         _("CRITICAL - Last modified %.1f days ago\n"),
+                         ((float) n) / (60 * 60 * 24));
+       else
+               die (STATE_CRITICAL,
+                   _("CRITICAL - Last modified %d:%02d:%02d ago\n"),
+                   n / (60 * 60), (n / 60) % 60, n % 60);
+    }
+
+    free (server_date);
+    free (document_date);
+  }
+}
+
+
 
 int
 check_http (void)
 {
        char *msg;
        char *status_line;
+       char *status_code;
        char *header;
        char *page;
        char *auth;
+       int http_status;
        int i = 0;
        size_t pagesize = 0;
        char *full_page;
@@ -484,7 +736,7 @@ check_http (void)
                        X509_free (server_cert);
                }
                else {
-                       printf (_("ERROR: Cannot retrieve server certificate.\n"));
+                       printf (_("CRITICAL - Cannot retrieve server certificate.\n"));
                        return STATE_CRITICAL;
                }
 
@@ -497,15 +749,17 @@ check_http (void)
        }
 #endif
 
-       asprintf (&buf, "%s %s HTTP/1.0\r\n", http_method, server_url);
+       asprintf (&buf, "%s %s HTTP/1.0\r\n%s\r\n", http_method, server_url, user_agent);
 
-       /* optionally send the host header info (not clear if it's usable) */
+       /* optionally send the host header info */
        if (host_name)
                asprintf (&buf, "%sHost: %s\r\n", buf, host_name);
 
-       /* send user agent */
-       asprintf (&buf, "%sUser-Agent: check_http/%s (nagios-plugins %s)\r\n",
-                 buf, clean_revstring (revision), VERSION);
+       /* optionally send any other header tag */
+       if (http_opt_headers) {
+               for ((pos = strtok(http_opt_headers, INPUT_DELIMITER)); pos; (pos = strtok(NULL, INPUT_DELIMITER)))
+                       asprintf (&buf, "%s%s\r\n", buf, pos);
+       }
 
        /* optionally send the authentication info */
        if (strlen(user_auth)) {
@@ -515,7 +769,11 @@ check_http (void)
 
        /* either send http POST data */
        if (http_post_data) {
-               asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
+               if (http_content_type) {
+                       asprintf (&buf, "%sContent-Type: %s\r\n", buf, http_content_type);
+               } else {
+                       asprintf (&buf, "%sContent-Type: application/x-www-form-urlencoded\r\n", buf);
+               }
                asprintf (&buf, "%sContent-Length: %i\r\n\r\n", buf, strlen (http_post_data));
                asprintf (&buf, "%s%s%s", buf, http_post_data, CRLF);
        }
@@ -524,6 +782,9 @@ check_http (void)
                asprintf (&buf, "%s%s", buf, CRLF);
        }
 
+       if (verbose)
+               printf ("%s\n", buf);
+
 #ifdef HAVE_SSL
        if (use_ssl == TRUE) {
                if (SSL_write (ssl, buf, (int)strlen(buf)) == -1) {
@@ -544,6 +805,11 @@ check_http (void)
                buffer[i] = '\0';
                asprintf (&full_page, "%s%s", full_page, buffer);
                pagesize += i;
+
+                if (no_body && document_headers_done (full_page)) {
+                  i = 0;
+                  break;
+                }
        }
 
        if (i < 0 && errno != ECONNRESET) {
@@ -553,12 +819,12 @@ check_http (void)
                        if ( sslerr == SSL_ERROR_SSL ) {
                                die (STATE_WARNING, _("Client Certificate Required\n"));
                        } else {
-                               die (STATE_CRITICAL, _("Error in recv()\n"));
+                               die (STATE_CRITICAL, _("Error on receive\n"));
                        }
                }
                else {
 #endif
-                       die (STATE_CRITICAL, _("Error in recv()\n"));
+                       die (STATE_CRITICAL, _("Error on receive\n"));
 #ifdef HAVE_SSL
                }
 #endif
@@ -578,7 +844,9 @@ check_http (void)
        page = full_page;
 
        if (verbose)
-               printf ("%s://%s:%d%s is %d characters\n", server_type, server_address, server_port, server_url, pagesize);
+               printf ("%s://%s:%d%s is %d characters\n",
+                       use_ssl ? "https" : "http", server_address,
+                       server_port, server_url, pagesize);
 
        /* find status line and null-terminate it */
        status_line = page;
@@ -604,57 +872,55 @@ check_http (void)
        page += (size_t) strspn (page, "\r\n");
        header[pos - header] = 0;
        if (verbose)
-               printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header, page);
+               printf ("**** HEADER ****\n%s\n**** CONTENT ****\n%s\n", header,
+                (no_body ? "  [[ skipped ]]" : page));
 
        /* make sure the status line matches the response we are looking for */
        if (!strstr (status_line, server_expect)) {
                if (server_port == HTTP_PORT)
-                       asprintf (&msg, _("Invalid HTTP response received from host\n"));
+                       asprintf (&msg,
+                           _("Invalid HTTP response received from host\n"));
                else
                        asprintf (&msg,
-                                       _("Invalid HTTP response received from host on port %d\n"),
-                                       server_port);
+                                 _("Invalid HTTP response received from host on port %d\n"),
+                                 server_port);
                die (STATE_CRITICAL, "%s", msg);
        }
 
-
        /* Exit here if server_expect was set by user and not default */
        if ( server_expect_yn  )  {
-               asprintf (&msg, _("HTTP OK: Status line output matched \"%s\"\n"),
-                         server_expect);
+               asprintf (&msg,
+                         _("HTTP OK: Status line output matched \"%s\"\n"),
+                         server_expect);
                if (verbose)
                        printf ("%s\n",msg);
-
        }
        else {
-       
+               /* Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
+               /* HTTP-Version   = "HTTP" "/" 1*DIGIT "." 1*DIGIT */
+    /* Status-Code = 3 DIGITS */
+
+               status_code = strchr (status_line, ' ') + sizeof (char);
+               if (strspn (status_code, "1234567890") != 3)
+                       die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status Line (%s)\n"), status_line);
+
+               http_status = atoi (status_code);
 
                /* check the return code */
+
+               if (http_status >= 600 || http_status < 100)
+                       die (STATE_CRITICAL, _("HTTP CRITICAL: Invalid Status (%s)\n"), status_line);
+
                /* server errors result in a critical state */
-               if (strstr (status_line, "500") || strstr (status_line, "501") ||
-                   strstr (status_line, "502") || strstr (status_line, "503") ||
-                   strstr (status_line, "504") || strstr (status_line, "505")) {
+               else if (http_status >= 500)
                        die (STATE_CRITICAL, _("HTTP CRITICAL: %s\n"), status_line);
-               }
 
                /* client errors result in a warning state */
-               if (strstr (status_line, "400") || strstr (status_line, "401") ||
-                   strstr (status_line, "402") || strstr (status_line, "403") ||
-                   strstr (status_line, "404") || strstr (status_line, "405") ||
-                   strstr (status_line, "406") || strstr (status_line, "407") ||
-                   strstr (status_line, "408") || strstr (status_line, "409") ||
-                   strstr (status_line, "410") || strstr (status_line, "411") ||
-                   strstr (status_line, "412") || strstr (status_line, "413") ||
-                   strstr (status_line, "414") || strstr (status_line, "415") ||
-                   strstr (status_line, "416") || strstr (status_line, "417")) {
+               else if (http_status >= 400)
                        die (STATE_WARNING, _("HTTP WARNING: %s\n"), status_line);
-               }
 
                /* check redirected page if specified */
-               if (strstr (status_line, "300") || strstr (status_line, "301") ||
-                   strstr (status_line, "302") || strstr (status_line, "303") ||
-                   strstr (status_line, "304") || strstr (status_line, "305") ||
-                   strstr (status_line, "306")) {
+               else if (http_status >= 300) {
 
                        if (onredirect == STATE_DEPENDENT)
                                redir (header, status_line);
@@ -668,22 +934,27 @@ check_http (void)
                                printf (_("CRITICAL"));
                        microsec = deltime (tv);
                        elapsed_time = (double)microsec / 1.0e6;
-                       asprintf (&msg, _(" - %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
-                                status_line, elapsed_time, timestamp,
-                          (display_html ? "</A>" : ""), microsec, pagesize);
-                       die (onredirect, "%s", msg);
-               } /* end if (strstr (status_line, "30[0-4]") */
-
+                       die (onredirect,
+                            _(" - %s - %.3f second response time %s%s|%s %s\n"),
+                            status_line, elapsed_time, timestamp,
+                            (display_html ? "</A>" : ""),
+                                        perfd_time (elapsed_time), perfd_size (pagesize));
+               } /* end if (http_status >= 300) */
 
        } /* end else (server_expect_yn)  */
-
                
+        if (maximum_age >= 0) {
+          check_document_dates (header);
+        }
+
        /* check elapsed time */
        microsec = deltime (tv);
        elapsed_time = (double)microsec / 1.0e6;
-       asprintf (&msg, _("HTTP problem: %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
-                      status_line, elapsed_time, timestamp,
-                      (display_html ? "</A>" : ""), microsec, pagesize);
+       asprintf (&msg,
+                 _("HTTP WARNING: %s - %.3f second response time %s%s|%s %s\n"),
+                 status_line, elapsed_time, timestamp,
+                 (display_html ? "</A>" : ""),
+                                               perfd_time (elapsed_time), perfd_size (pagesize));
        if (check_critical_time == TRUE && elapsed_time > critical_time)
                die (STATE_CRITICAL, "%s", msg);
        if (check_warning_time == TRUE && elapsed_time > warning_time)
@@ -694,14 +965,16 @@ check_http (void)
 
        if (strlen (string_expect)) {
                if (strstr (page, string_expect)) {
-                       printf (_("HTTP OK %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
+                       printf (_("HTTP OK %s - %.3f second response time %s%s|%s %s\n"),
                                status_line, elapsed_time,
-                               timestamp, (display_html ? "</A>" : ""), microsec, pagesize);
+                               timestamp, (display_html ? "</A>" : ""),
+                               perfd_time (elapsed_time), perfd_size (pagesize));
                        exit (STATE_OK);
                }
                else {
-                       printf (_("CRITICAL - string not found%s|time=%ldus\n size=%dB"),
-                               (display_html ? "</A>" : ""), microsec, pagesize);
+                       printf (_("CRITICAL - string not found%s|%s %s\n"),
+                               (display_html ? "</A>" : ""),
+                               perfd_time (elapsed_time), perfd_size (pagesize));
                        exit (STATE_CRITICAL);
                }
        }
@@ -709,15 +982,17 @@ check_http (void)
        if (strlen (regexp)) {
                errcode = regexec (&preg, page, REGS, pmatch, 0);
                if (errcode == 0) {
-                       printf (_("HTTP OK %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
+                       printf (_("HTTP OK %s - %.3f second response time %s%s|%s %s\n"),
                                status_line, elapsed_time,
-                               timestamp, (display_html ? "</A>" : ""), microsec, pagesize);
+                               timestamp, (display_html ? "</A>" : ""),
+                               perfd_time (elapsed_time), perfd_size (pagesize));
                        exit (STATE_OK);
                }
                else {
                        if (errcode == REG_NOMATCH) {
-                               printf (_("CRITICAL - pattern not found%s|time=%ldus size=%dB\n"),
-                                       (display_html ? "</A>" : ""), microsec, pagesize);
+                               printf (_("CRITICAL - pattern not found%s|%s %s\n"),
+                                       (display_html ? "</A>" : ""),
+                                       perfd_time (elapsed_time), perfd_size (pagesize));
                                exit (STATE_CRITICAL);
                        }
                        else {
@@ -732,21 +1007,21 @@ check_http (void)
        /* make sure the page is of an appropriate size */
        page_len = strlen (page);
        if ((min_page_len > 0) && (page_len < min_page_len)) {
-               printf (_("HTTP WARNING: page size too small%s|size=%i\n"),
-                       (display_html ? "</A>" : ""), page_len );
+               printf (_("HTTP WARNING: page size %d too small%s|%s\n"),
+                       page_len, (display_html ? "</A>" : ""), perfd_size (page_len) );
                exit (STATE_WARNING);
        }
        /* We only get here if all tests have been passed */
-       asprintf (&msg, _("HTTP OK %s - %.3f second response time %s%s|time=%ldus size=%dB\n"),
-                       status_line, elapsed_time,
-                       timestamp, (display_html ? "</A>" : ""), microsec, pagesize);
+       asprintf (&msg, _("HTTP OK %s - %d bytes in %.3f seconds %s%s|%s %s\n"),
+                 status_line, page_len, elapsed_time,
+                 timestamp, (display_html ? "</A>" : ""),
+                                               perfd_time (elapsed_time), perfd_size (page_len));
        die (STATE_OK, "%s", msg);
        return STATE_UNKNOWN;
 }
 
 
 
-
 /* per RFC 2396 */
 #define HDR_LOCATION "%*[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]: "
 #define URI_HTTP "%[HTPShtps]://"
@@ -759,7 +1034,7 @@ check_http (void)
 #define HD4 URI_HTTP URI_HOST
 #define HD5 URI_PATH
 
-int
+void
 redir (char *pos, char *status_line)
 {
        int i = 0;
@@ -772,77 +1047,79 @@ redir (char *pos, char *status_line)
 
        addr = malloc (MAX_IPV4_HOSTLENGTH + 1);
        if (addr == NULL)
-               die (STATE_UNKNOWN, _("ERROR: could not allocate addr\n"));
+               die (STATE_UNKNOWN, _("Could not allocate addr\n"));
        
        url = malloc (strcspn (pos, "\r\n"));
        if (url == NULL)
-               die (STATE_UNKNOWN, _("ERROR: could not allocate url\n"));
+               die (STATE_UNKNOWN, _("Could not allocate url\n"));
 
        while (pos) {
 
-               if (sscanf (pos, "%[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]:%n", xx, &i) > 0) {
+               if (sscanf (pos, "%[Ll]%*[Oo]%*[Cc]%*[Aa]%*[Tt]%*[Ii]%*[Oo]%*[Nn]:%n", xx, &i) < 1) {
 
-                       pos += i;
-                       pos += strspn (pos, " \t\r\n");
-
-                       /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
-                       if (sscanf (pos, HD1, type, addr, port, url) == 4) {
-                               use_ssl = server_type_check (type);
-                               i = atoi (port);
-                       }
-
-                       /* URI_HTTP URI_HOST URI_PATH */
-                       else if (sscanf (pos, HD2, type, addr, url) == 3 ) { 
-                               use_ssl = server_type_check (type);
-                               i = server_port_check (use_ssl);
-                       }
+                       pos += (size_t) strcspn (pos, "\r\n");
+                       pos += (size_t) strspn (pos, "\r\n");
+                       if (strlen(pos) == 0) 
+                               die (STATE_UNKNOWN,
+                                                _("UNKNOWN - Could not find redirect location - %s%s\n"),
+                                                status_line, (display_html ? "</A>" : ""));
+                       continue;
+               }
 
-                       /* URI_HTTP URI_HOST URI_PORT */
-                       else if(sscanf (pos, HD3, type, addr, port) == 3) {
-                               strcpy (url, HTTP_URL);
-                               use_ssl = server_type_check (type);
-                               i = atoi (port);
-                       }
+               pos += i;
+               pos += strspn (pos, " \t\r\n");
 
-                       /* URI_HTTP URI_HOST */
-                       else if(sscanf (pos, HD4, type, addr) == 2) {
-                               strcpy (url, HTTP_URL);
-                               use_ssl = server_type_check (type);
-                               i = server_port_check (use_ssl);
-                       }
+               url = realloc (url, strcspn (pos, "\r\n"));
+               if (url == NULL)
+                       die (STATE_UNKNOWN, _("could not allocate url\n"));
 
-                       /* URI_PATH */
-                       else if (sscanf (pos, HD5, url) == 1) {
-                               /* relative url */
-                               if ((url[0] != '/')) {
-                                       if ((x = strrchr(url, '/')))
-                                               *x = '\0';
-                                       asprintf (&server_url, "%s/%s", server_url, url);
-                               }
-                               i = server_port;
-                               strcpy (type, server_type);
-                               strcpy (addr, host_name);
-                       }                                       
+               /* URI_HTTP, URI_HOST, URI_PORT, URI_PATH */
+               if (sscanf (pos, HD1, type, addr, port, url) == 4) {
+                       use_ssl = server_type_check (type);
+                       i = atoi (port);
+               }
 
-                       else {
-                               die (STATE_UNKNOWN,
-                                                _("UNKNOWN - Could not parse redirect location - %s%s\n"),
-                                                pos, (display_html ? "</A>" : ""));
-                       }
+               /* URI_HTTP URI_HOST URI_PATH */
+               else if (sscanf (pos, HD2, type, addr, url) == 3 ) { 
+                       use_ssl = server_type_check (type);
+                       i = server_port_check (use_ssl);
+               }
 
-                       break;
+               /* URI_HTTP URI_HOST URI_PORT */
+               else if(sscanf (pos, HD3, type, addr, port) == 3) {
+                       strcpy (url, HTTP_URL);
+                       use_ssl = server_type_check (type);
+                       i = atoi (port);
+               }
 
-               } else {
+               /* URI_HTTP URI_HOST */
+               else if(sscanf (pos, HD4, type, addr) == 2) {
+                       strcpy (url, HTTP_URL);
+                       use_ssl = server_type_check (type);
+                       i = server_port_check (use_ssl);
+               }
 
-                       pos += (size_t) strcspn (pos, "\r\n");
-                       pos += (size_t) strspn (pos, "\r\n");
-                       if (strlen(pos) == 0) 
-                               die (STATE_UNKNOWN,
-                                                _("UNKNOWN - Could not find redirect location - %s%s\n"),
-                                                status_line, (display_html ? "</A>" : ""));
+               /* URI_PATH */
+               else if (sscanf (pos, HD5, url) == 1) {
+                       /* relative url */
+                       if ((url[0] != '/')) {
+                               if ((x = strrchr(server_url, '/')))
+                                       *x = '\0';
+                               asprintf (&url, "%s/%s", server_url, url);
+                       }
+                       i = server_port;
+                       strcpy (type, server_type);
+                       strcpy (addr, host_name);
+               }                                       
 
+               else {
+                       die (STATE_UNKNOWN,
+                                        _("UNKNOWN - Could not parse redirect location - %s%s\n"),
+                                        pos, (display_html ? "</A>" : ""));
                }
 
+               break;
+
        } /* end while (pos) */
 
        if (++redir_depth > max_depth)
@@ -860,11 +1137,17 @@ redir (char *pos, char *status_line)
 
        server_port = i;
        strcpy (server_type, type);
-       asprintf (&host_name, "%s", addr);
-       asprintf (&server_address, "%s", addr);
-       asprintf (&server_url, "%s", url);
 
-       return check_http ();
+       free (host_name);
+       host_name = strdup (addr);
+
+       free (server_address);
+       server_address = strdup (addr);
+
+       free (server_url);
+       server_url = strdup (url);
+
+       check_http ();
 }
 
 
@@ -940,6 +1223,8 @@ int connect_SSL (void)
 }
 #endif
 
+
+
 #ifdef HAVE_SSL
 int
 check_certificate (X509 ** certificate)
@@ -1015,7 +1300,27 @@ check_certificate (X509 ** certificate)
        return STATE_OK;
 }
 #endif
-\f
+
+
+
+char *perfd_time (double elapsed_time)
+{
+       return fperfdata ("time", elapsed_time, "s",
+                 check_warning_time, warning_time,
+                 check_critical_time, critical_time,
+                                                                        TRUE, 0, FALSE, 0);
+}
+
+
+
+char *perfd_size (int page_len)
+{
+       return perfdata ("size", page_len, "B",
+                 (min_page_len>0?TRUE:FALSE), min_page_len,
+                 (min_page_len>0?TRUE:FALSE), 0,
+                 TRUE, 0, FALSE, 0);
+}
+
 
 
 int
@@ -1036,6 +1341,7 @@ my_recv (void)
 }
 
 
+
 int
 my_close (void)
 {
@@ -1056,22 +1362,19 @@ my_close (void)
 
 
 
-
-
-\f
 void
 print_help (void)
 {
        print_revision (progname, revision);
 
-       printf (_("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n"));
-       printf (_(COPYRIGHT), copyright, email);
+       printf ("Copyright (c) 1999 Ethan Galstad <nagios@nagios.org>\n");
+       printf (COPYRIGHT, copyright, email);
 
        printf (_("\
 This plugin tests the HTTP service on the specified host. It can test\n\
 normal (http) and secure (https) servers, follow redirects, search for\n\
 strings and regular expressions, check connection times, and report on\n\
-certificate expiration times.\n"));
+certificate expiration times.\n\n"));
 
        print_usage ();
 
@@ -1082,6 +1385,7 @@ certificate expiration times.\n"));
        printf (_("\
  -H, --hostname=ADDRESS\n\
     Host name argument for servers using host headers (virtual host)\n\
+    Append a port to include it in the header (eg: example.com:5000)\n\
  -I, --IP-address=ADDRESS\n\
    IP address or name (use numeric address if possible to bypass DNS lookup).\n\
  -p, --port=INTEGER\n\
@@ -1107,7 +1411,15 @@ certificate expiration times.\n"));
  -u, --url=PATH\n\
    URL to GET or POST (default: /)\n\
  -P, --post=STRING\n\
-   URL encoded http POST data\n"), HTTP_EXPECT);
+   URL encoded http POST data\n\
+ -N, --no-body\n\
+   Don't wait for document body: stop reading after headers.\n\
+   (Note that this still does an HTTP GET or POST, not a HEAD.)\n\
+ -M, --max-age=SECONDS\n\
+   Warn if document is more than SECONDS old. the number can also be of \n\
+   the form \"10m\" for minutes, \"10h\" for hours, or \"10d\" for days.\n\
+ -T, --content-type=STRING\n\
+   specify Content-Type header media type when POSTing\n"), HTTP_EXPECT);
 
 #ifdef HAVE_REGEX_H
        printf (_("\
@@ -1122,6 +1434,10 @@ certificate expiration times.\n"));
        printf (_("\
  -a, --authorization=AUTH_PAIR\n\
    Username:password on sites with basic authentication\n\
+ -A, --useragent=STRING\n\
+   String to be sent in http header as \"User Agent\"\n\
+ -k, --header=STRING\n\
+   Any other tags to be sent in http header, separated by semicolon\n\
  -L, --link=URL\n\
    Wrap output in HTML link (obsoleted by urlize)\n\
  -f, --onredirect=<ok|warning|critical|follow>\n\
@@ -1169,15 +1485,14 @@ the certificate is expired.\n"));
 
 
 
-
 void
 print_usage (void)
 {
-       printf (_("\
-Usage: %s (-H <vhost> | -I <IP-address>) [-u <uri>] [-p <port>]\n\
-  [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\
-  [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\
-  [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n\
-  [-P string] [-m min_pg_size] [-4|-6]\n"), progname);
-       printf (_(UT_HLP_VRS), progname, progname);
+       printf ("\
+Usage: %s -H <vhost> | -I <IP-address>) [-u <uri>] [-p <port>]\n\
+                  [-w <warn time>] [-c <critical time>] [-t <timeout>] [-L]\n\
+                  [-a auth] [-f <ok | warn | critcal | follow>] [-e <expect>]\n\
+                  [-s string] [-l] [-r <regex> | -R <case-insensitive regex>]\n\
+                  [-P string] [-m min_pg_size] [-4|-6] [-N] [-M <age>]\n\
+                  [-A string] [-k string]\n", progname);
 }