#! /usr/bin/bash
#
# rfc
#
# Author: Baptiste Fontaine
# License: MIT
# Version: 0.2.6 (2019/05/11)
#
# URL: https://github.com/bfontaine/rfc
#

__rfc() {

    local VERSION='0.2.5'
    local PAGER=${PAGER:-less}
    local fetch_cmd=${CURL:-curl}
    local rfc_dir="${RFC_DIR:-$HOME/.RFCs}"
    local code=

    # Error codes
    local NOT_FOUND=1
    local UNRECOGNIZED_CMD=2
    local NETWORK_ERROR=3
    local NO_CURL_WGET=4

    # URLs
    local RFC_REMOTE_URL='https://raw.github.com/bfontaine/rfc/master/rfc'
    local REPO_HOME='https://github.com/bfontaine/rfc'
    local ISSUES_URL='https://github.com/bfontaine/rfc/issues'

    local RFCS_TGZ_BASEURL='https://www.rfc-editor.org/in-notes/tar/'
    local RFCS_BASEURL='https://www.rfc-editor.org/rfc/'
    local DRAFTS_BASEURL='https://www.ietf.org/id/'

    # $rfc_dir must be absolute
    [ "${rfc_dir:0:1}" != "/" ] && rfc_dir="$PWD/$rfc_dir"

    print_rfc() {
        $PAGER "$rfc_dir/$1"
    }

    fetch_url() {
        case $fetch_cmd in
            curl)
                curl -fs "$1" >| "$2";
                case "$?" in
                    6|7) return $NETWORK_ERROR ;;
                    22)  return $NOT_FOUND ;;
                    *)   return 0 ;;
                esac ;;

            wget)
                wget -cq "$1" -O "$2";
                case "$?" in
                    4) return $NETWORK_ERROR ;;
                    8) return $NOT_FOUND ;;
                    *) return 0 ;;
                esac ;;
        esac
    }

    get_ftp_url() {
        local tmp=$(mktemp)
        # $1 = pattern of the file we're looking for
        local pattern=$1
        local fetch_exit_code=

        fetch_url "$RFCS_TGZ_BASEURL" "$tmp"
        fetch_exit_code=$?

        if [ "$fetch_exit_code" -ne 0 ]; then
          return $fetch_exit_code
        fi

        # Lines have the following format:
        #   <li><a href="RFCs0001-0500.tar.gz"> RFCs0001-0500.tar.gz</a></li>
        # We want this __^^^^^^^^^^^^^^^^^^^^
        cat "$tmp" | grep "$pattern" | cut -d'"' -f2
    }

    get_rfc() {
        fetch_url "$RFCS_BASEURL/rfc${1}.txt" "$rfc_dir/${1}"
        return $?
    }

    get_draft() {
        fetch_url "$DRAFTS_BASEURL/${1}.txt" "$rfc_dir/${1}"
        return $?
    }

    init_rfc_dir() {
        mkdir -p $rfc_dir
        touch $rfc_dir/_404s
    }

    print_not_found_error() {
        if [[ $1 == draft* ]]; then
            echo "There's no such draft."
        else
            echo "There's no such RFC."
        fi
    }

    ## Subcommands ##

    # rfc list
    list() {
        \ls -1 $rfc_dir | grep '^[0-9]\{1,\}$' | sort -n | sed 's/^/RFC /'
    }

    # rfc search <pattern>
    search() {
        if [ -z "$1" ]; then
            echo 'Usage: rfc search "<pattern>"'
            return $UNRECOGNIZED_CMD;
        fi
        grep -RIs --exclude _404s $@ "$rfc_dir" | sed "s%$rfc_dir/%RFC %"
        return 0
    }

    # rfc sync [week|month|all]
    sync_rfcs() {
        local name=RFC-all.tar.gz
        case "$1" in
            month|30|30days) name=$(get_ftp_url "30daysTo.*gz") ;;
            week|7|7days)    name=$(get_ftp_url "7daysTo.*gz") ;;
            all) ;;
            *)
              # Support 'sync' without any argument as an alias to 'sync all'
              if [ ! -z "$1" ]; then
                echo "Unrecognized 'sync' target: $1"
                return $UNRECOGNIZED_CMD
              fi ;;
        esac
        if [ $? -ne 0 ]; then
          return $?
        fi

        # This shouldn't happen.
        if [ "x$name" == "x" ]; then
          echo "I couldn't find the requested archive." >&2
          echo "Please report this: $ISSUES_URL" >&2
          return $NOT_FOUND
        fi

        mkdir -p "$rfc_dir/_tmp"
        fetch_url "$RFCS_TGZ_BASEURL$name" "$rfc_dir/_tmp/$name"
        [ "$?" -ne "0" ] && exit $NETWORK_ERROR
        cd "$rfc_dir/_tmp"
        tar -xzf $name
        rm -f $name
        [ -d in-notes ] && cd in-notes
        for f in `\ls -1 rfc*.txt`; do
            echo "$f" | grep -q '^rfc[0-9]\{1,\}\.txt$'
            if [ "$?" -eq "0" ]; then
                n=${f##rfc};
                n=${n%%.txt};
                mv -f $f $rfc_dir/$n
            fi
        done
        rm -rf "$rfc_dir/_tmp"
    }

    # rfc update (deprecated)
    update() {
        fetch_url $RFC_REMOTE_URL $0
        chmod +x "$0"
        return 0
    }

    # rfc help
    print_usage() {
        echo 'Usage:
    rfc --version             # display the version number and exit
    rfc --help                # display this text and exit

    rfc <number>              # display the RFC <number>
    rfc search [OPTS] X       # Search for X in local RFCs using grep with OPTS
                                passed through
    rfc list                  # List locally available RFCs
    rfc sync [week|month|all] # batch download RFCs. `week` and `month`
                                respectively download only RFCs that were
                                added/updated during the last week/month.
                                `all` (default) downloads all RFCs. It might
                                take some time, be patient.
    rfc clear                 # clear the cache'
    }

    # rfc version
    print_version() {
        echo "rfc v$VERSION - $REPO_HOME"
    }

    ## /commands ##

    if [ $# -eq 0 ]; then
        print_usage
        return 0
    fi

    # Prefix everything with --debug to enable the debugging output on STDERR.
    if [ "$1" == "--debug" ]; then
      shift ;
      echo "Enabling debug mode." >&2 ;
      set -x ;
    fi

    which curl > /dev/null 2>&1
    if [ $? -ne 0 ]; then
        which wget > /dev/null 2>&1

        if [ $? -ne 0 ]; then
            echo "Error: You need Wget or cURL!"
            return $NO_CURL_WGET;
        fi

        fetch_cmd='wget'

    fi

    init_rfc_dir

    case "$1" in

        -v|--version|version)
            print_version
            return 0;;

        -h|--help|-help|help)
            print_usage
            return 0;;

        -*)
            echo "Unrecognized option: $1"
            print_usage
            return $UNRECOGNIZED_CMD;;

        clear|clean|clr)
            rm -rf $rfc_dir
            return 0;;

        up|update)
            update
            return 0;;

        ls|list)
            list
            return 0;;

        search|find)
            shift;
            search $*
            return $?;;

        sync|download)
            shift;
            sync_rfcs $*
            return $?;;

        _*)
            print_usage
            return 1;;

        *)
        grep -q "^$1\$" "$rfc_dir/_404s" 2> /dev/null
        if [ $? -eq 0 ]; then

            print_not_found_error "$1"
            return $NOT_FOUND

        elif [ ! -f "$rfc_dir/$1" ]; then

            if [[ $1 == draft* ]]; then
                get_draft $1
            else
                get_rfc $1
            fi

            code=$?

            if [ $code -eq $NETWORK_ERROR ]; then
                echo "Unable to connect to the network."
                [ ! -s "$rfc_dir/$1" ] && rm -f "$rfc_dir/$1"
                return $NETWORK_ERROR
            fi

            if [ $code -eq $NOT_FOUND ]; then
                echo "$1" >> "$rfc_dir/_404s"
                print_not_found_error "$1"

                [ ! -s "$rfc_dir/$1" ] && rm -f "$rfc_dir/$1"

                return $NOT_FOUND
            fi

        fi

        print_rfc $* ;;
    esac

}

__rfc $*

