#!/usr/bin/python -u
# -*- coding: iso-8859-1 -*-
# Copyright (C) 2012 Bastian Kleineidam
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""
Check HTML pages for broken links. This is the commandline nagios plugin.
Run this file without options to see how it's done.
"""

import sys
import os
import socket
import optparse
import pprint
# installs _() and _n() gettext functions into global namespace
import linkcheck
# override optparse gettext method with the one from linkcheck.init_i18n()
optparse._ = _
# now import the rest of the linkchecker gang
from linkcheck.cmdline import print_version, print_usage, LCOptionParser, \
  aggregate_url
from linkcheck import log, LOG_CMDLINE, strformat
import linkcheck.checker
import linkcheck.configuration
import linkcheck.fileutil
import linkcheck.logger
import linkcheck.ansicolor
from linkcheck.director import console, check_urls, get_aggregate

# usage texts
Usage = _("""USAGE\tlinkchecker-nagios [options] [file-or-url]...""")

Retval = _(r"""RETURN VALUE
0 - everything checked ok without errors or warnings
1 - URL check warnings were found
2 - URL check errors were found
3 - internal or config error
""")

Examples = _(r"""EXAMPLES
  linkchecker-nagios -v www.example.org
""")


class MyOptionParser (LCOptionParser):
    """Option parser for LinkChecker nagios plugin."""

    def get_usage (self):
        """Return translated usage text."""
        return Usage

    def print_help (self, file=None):
        """Print translated help text."""
        s = u"%s\n%s\n%s" % (self.format_help(), Examples, Retval)
        if file is None:
            file = sys.stdout
        self.print_help_msg(s, file)

# instantiate option parser and configure options
optparser = MyOptionParser(err_exit_code=3)

# build a config object for this check session
config = linkcheck.configuration.Configuration()
# default recursion level is 1
config['recursionlevel'] = 1

# Standard options
group = optparse.OptionGroup(optparser, _("Standard nagios options"))
group.add_option("-v", "--verbose", action="count", default=0, dest="verbose",
  help=_("""Increase verbosity. This option can be given multiple times."""))
group.add_option("-V", "--version", action="store_true", dest="version",
                 help=_("""Print version and exit."""))
group.add_option("-t", "--timeout", type="int", dest="timeout",
                 metavar="NUMBER",
                 help=_(
"""Set the timeout for connection attempts in seconds. The default
timeout is %d seconds.""") % config["timeout"])

# Checking options
group = optparse.OptionGroup(optparser, _("Checking options"))
group.add_option("-f", "--config", type="string", dest="configfile",
                 metavar="FILENAME",
                 help=_(
"""Use FILENAME as configuration file. Per default LinkChecker uses
~/.linkchecker/linkcheckerrc (under Windows
%HOMEPATH%\\.linkchecker\\linkcheckerrc)."""))


# read and parse command line options and arguments
(options, args) = optparser.parse_args()

if options.version is not None:
    print_version()
if options.timeout is not None:
    if options.timeout > 0:
        config["timeout"] = options.timeout
    else:
        print_usage(_("Illegal argument %(arg)r for option %(option)s") % \
                    {"arg": options.timeout, "option": "'--timeout'"},
                    exit_code=3)
socket.setdefaulttimeout(config["timeout"])
if options.verbose >= 3:
    debug = ['all']
else:
    debug = None
# initialize logging
config.init_logging(console.StatusLogger(), debug=debug)
log.debug(LOG_CMDLINE, _("Python %(version)s on %(platform)s") % \
   {"version": sys.version, "platform": sys.platform})
# read configuration files
try:
    files = []
    if options.configfile:
        path = linkcheck.configuration.normpath(options.configfile)
        if os.path.isfile(path):
            files.append(path)
        else:
            log.warn(LOG_CMDLINE,
                      _("Unreadable config file: %r"), options.configfile)
            sys.exit(3)
    config.read(files=files)
except linkcheck.LinkCheckerError, msg:
    # config error
    print_usage(str(msg), exit_code=3)
linkcheck.drop_privileges()


socket.setdefaulttimeout(config["timeout"])
if options.verbose < 0:
    config['logger'] = config.logger_new('none')
else:
    config["verbose"] = True
    config["warnings"] = True
    if options.verbose >= 2:
        config["complete"] = True

# check missing passwords
for entry in config["authentication"]:
    if entry["password"] is None:
        pattern = entry["pattern"].pattern
        log.warn(LOG_CMDLINE, _("missing password for pattern %(pattern)s") % dict(pattern=pattern))
        sys.exit(3)
# sanitize the configuration
config.sanitize()

log.debug(LOG_CMDLINE, "configuration: %s",
          pprint.pformat(sorted(config.items())))
# prepare checking queue
aggregate = get_aggregate(config)
# add urls to queue
if args:
    for url in args:
        aggregate_url(aggregate, strformat.stripurl(url), err_exit_code=3)
else:
    log.warn(LOG_CMDLINE, _("no files or URLs given"))
    sys.exit(3)

# finally, start checking
check_urls(aggregate)

stats = config['logger'].stats
# on internal errors, exit with status 3
if stats.internal_errors:
    sys.exit(3)
# on errors, exit with status 2
if stats.errors:
    sys.exit(2)
# on warnings, exit with status 1
if stats.warnings_printed and config['warnings']:
    sys.exit(1)
