#!/bin/sh
# apt-config outputs APT configuration data
# Copyright (C) 2010 Loïc Minier <lool@dooz.org>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
# SOFTWARE IN THE PUBLIC INTEREST, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
# Except as contained in this notice, the name of the author shall not be used
# in advertising or otherwise to promote the sale, use or other dealings in
# this Software without prior written authorization from the author.
#
# TODO figure a way to integrate PPA support in profile mode

set -e

self="$(basename "$0")"

log() {
    echo "$@" >&2
}

usage() {
    log "$self [<options>] [--archive=<archive>] [--mirror=<mirror>] [--suite=<suite>] [--pockets=<pocket1>,<pocket2>] <action>"
    log "$self [--profile=<profile>] <action>"
    log "  where <action> is one of apt-sources, debootstrap-mirror, or debootstrap-suite"
    log
    log "Common options:"
    log "  --with-sources, --with-sources=[yes|no|disabled]"
    log "  --mirror-map=<mirror-map>"
    log "  --arch=<arch>"
    log "  --components=<comp1>,<comp2>"
}

die() {
    log "$@"
    exit 1
}

guess_dist_arch_archive() {
    local dist="$1"
    local arch="$2"

    case "$dist" in
      etch|lenny|squeeze|sid|oldstable|stable|testing|unstable|experimental)
        echo "debian"
      ;;
      buzz|rex|bo|hamm|slink|potato|woody|sarge|etch|lenny|squeeze|wheezy|jessie|stretch|buster)
        echo "debian-archive"
      ;;
      precise|trusty|xenial|yakkety|zesty|artful)
        case "$arch" in
          amd64|i386)
            echo "ubuntu"
          ;;
          arm64|armel|armhf|hppa|ia64|lpia|powerpc|ppc64el|s390x|sparc)
            echo "ubuntu-ports"
          ;;
          *)
            die "Unknown Ubuntu archive for arch=$arch"
          ;;
        esac
      ;;
      warty|hoary|breezy|edgy|feisty|gutsy|dapper|hardy|intrepid|jaunty|karmic|lucid|maverick|natty|oneiric|quantal|raring|saucy|utopic|vivid|wily)
        echo "ubuntu-old"
      ;;
      *)
        die "Unknown archive for dist=$dist"
      ;;
    esac
}

get_archive_url() {
    local mirror_map="$1"
    local archive="$2"
    local url

    if [ -z "$archive" ]; then
        die "Need an archive in get_archive_url()"
    fi

    if [ -n "$mirror_map" ]; then
        while read a url; do
            case "$a" in
              "$archive")
                if [ -n "$url" ]; then
                    echo "$url"
                    return
                fi
              ;;
              \#*|""|*)
                :
              ;;
            esac
        done <"$mirror_map"
    fi
    builtin_mirror_map "$archive"
}

builtin_mirror_map() {
    local archive="$1"
    local people_ppa

    case "$archive" in
      backports.org)
        echo "http://www.backports.org/backports.org/"
      ;;
      debian)
        echo "http://deb.debian.org/debian/"
      ;;
      debian-archive)
        echo "http://archive.debian.org/debian/"
      ;;
      debian-security)
        echo "http://security.debian.org/"
      ;;
      debian-volatile)
        echo "http://volatile.debian.org/debian-volatile/"
      ;;
      ppa:*)
        people_ppa="${archive#ppa:}"
        case "$people_ppa" in
          */*)
            :
          ;;
          *)
            people_ppa="$people_ppa/ppa"
        esac
        echo "http://ppa.launchpad.net/$people_ppa/ubuntu/"
      ;;
      ubuntu)
        echo "http://archive.ubuntu.com/ubuntu/"
      ;;
      ubuntu-old)
        echo "http://old-releases.ubuntu.com/ubuntu/"
      ;;
      ubuntu-ports)
        echo "http://ports.ubuntu.com/ubuntu-ports/"
      ;;
      ubuntu-security)
        echo "http://security.ubuntu.com/ubuntu/"
      ;;
      *)
        die "Unknown mirror for archive=$archive"
      ;;
    esac
}

output_sources() {
    local with_sources="$1"
    local mirror="$2"
    local dist="$3"
    local components="$4"

    case "$with_sources" in
      yes)
        echo "deb     $mirror $dist $components"
        echo "deb-src $mirror $dist $components"
      ;;
      disabled)
        echo "deb     $mirror $dist $components"
        echo "#deb-src $mirror $dist $components"
      ;;
      no)
        echo "deb $mirror $dist $components"
      ;;
      *)
        die 'with_sources must be either "yes", "disabled", or "no"'
      ;;
    esac
}

sources=""
debootstrap_suite=""
debootstrap_mirror=""

add_output_sources() {
    local output="$(output_sources "$@")"

    sources="$sources${sources:+
}$output"
}

set_debootstrap_suite() {
    local archive="$1"
    local suite="$2"

    case "$archive" in
      debian)
        case "$suite" in
          unstable)
            debootstrap_suite="sid"
          ;;
          testing)
            debootstrap_suite="squeeze"
          ;;
          stable)
            debootstrap_suite="lenny"
          ;;
          oldstable)
            debootstrap_suite="etch"
          ;;
          *)
            debootstrap_suite="$suite"
          ;;
        esac
      ;;
      *)
        debootstrap_suite="$suite"
      ;;
    esac
}

getopt_output="$(getopt -o "" -l help,with-sources::,arch:,components:,archive:,mirror:,mirror-map:,suite:,pockets:,profile: -n "$self" -s sh -- "$@")"

eval set -- "$getopt_output"

with_sources="disabled"
arch="$(dpkg --print-architecture)"
components="main"
archive=""
mirror=""
mirror_map=""
suite=""
pockets=""
archive=""
profile=""

while :; do
    case "$1" in
      --help)
        usage
        exit 0
      ;;
      --with-sources)
        case "$2" in
          "")
            with_sources="yes"
          ;;
          yes|no|disabled)
            with_sources="$2"
          ;;
          *)
            die '--with-sources must be either "yes", "disabled", or "no"'
          ;;
        esac
        shift 2
      ;;
      --arch)
        arch="$2"
        shift 2
      ;;
      --components)
        components="$2"
        shift 2
      ;;
      --archive)
        archive="$2"
        shift 2
      ;;
      --mirror)
        mirror="$2"
        shift 2
      ;;
      --mirror-map)
        mirror_map="$2"
        shift 2
      ;;
      --suite)
        if [ -z "$2" ]; then
            die "Need a suite for --suite"
        fi
        if [ -n "$profile" ]; then
            die "Can't set --suite after --profile"
        fi
        suite="$2"
        shift 2
      ;;
      --pockets)
        pockets="$2"
        shift 2
      ;;
      --profile)
        if [ -z "$2" ]; then
            die "Need a profile for --profile"
        fi
        if [ -n "$suite" ]; then
            die "Can't set --profile after --suite"
        fi
        profile="$2"
        shift 2
      ;;
      --)
        shift
        break
      ;;
      *)
        die "Unhandled arg=$1"
      ;;
    esac
done

if [ -z "$suite" ] && [ -z "$profile" ]; then
    die "Please pass --suite or --profile"
fi

components="$(echo "$components" | tr , " ")"
pockets="$(echo "$pockets" | tr , " ")"

if [ -n "$profile" ]; then
    base_dist="${profile%%/*}"
    base_dist="${base_dist%%-*}"
    base_archive="$(guess_dist_arch_archive "$base_dist" "$arch")"
    base_mirror="$(get_archive_url "$mirror_map" "$base_archive")"
    case "$base_archive" in
      debian|debian-archive)
        case "$profile" in
          experimental)
            base_dist="unstable"
            add_output_sources "$with_sources" "$base_mirror" "$base_dist" "$components"
            add_output_sources "$with_sources" "$base_mirror" "$profile" "$components"
          ;;
          *-proposed-updates/volatile)
            archive="debian-volatile"
            mirror="$(get_archive_url "$mirror_map" "$archive")"
            volatile_dist="$base_dist/volatile"
            add_output_sources "$with_sources" "$base_mirror" "$base_dist" "$components"
            add_output_sources "$with_sources" "$mirror" "$volatile_dist" "$components"
            add_output_sources "$with_sources" "$mirror" "$profile" "$components"
          ;;
          */volatile|*/volatile-sloppy)
            if [ "$base_dist" != "${profile%%/*}" ]; then
                die "Unknown Debian Volatile based profile=$profile"
            fi
            archive="debian-volatile"
            mirror="$(get_archive_url "$mirror_map" "$archive")"
            volatile_dist=${profile%-sloppy}
            add_output_sources "$with_sources" "$base_mirror" "$base_dist" "$components"
            add_output_sources "$with_sources" "$mirror" "$volatile_dist" "$components"
            if [ "$volatile_dist" != "$profile" ]; then
                add_output_sources "$with_sources" "$mirror" "$profile" "$components"
            fi
          ;;
          *-backports)
            if [ "$base_dist" != "${profile%%-*}" ]; then
                die "Unknown Backports.org based profile=$profile"
            fi
            archive="backports.org"
            mirror="$(get_archive_url "$mirror_map" "$archive")"
            add_output_sources "$with_sources" "$base_mirror" "$base_dist" "$components"
            add_output_sources "$with_sources" "$mirror" "$profile" "$components"
          ;;
          */updates)
            if [ "$base_dist" != "${profile%%/*}" ]; then
                die "Unknown Debian Security based profile=$profile"
            fi
            archive="debian-security"
            mirror="$(get_archive_url "$mirror_map" "$archive")"
            add_output_sources "$with_sources" "$base_mirror" "$base_dist" "$components"
            add_output_sources "$with_sources" "$mirror" "$profile" "$components"
          ;;
          *-proposed-updates)
            if [ "$base_dist" != "${profile%%-*}" ]; then
                die "Unknown Debian Updates based profile=$profile"
            fi
            add_output_sources "$with_sources" "$base_mirror" "$base_dist" "$components"
            add_output_sources "$with_sources" "$base_mirror" "$profile" "$components"
          ;;
          *)
            if [ "$base_dist" != "$profile" ]; then
                die "Unknown Debian based profile=$profile"
            fi
            add_output_sources "$with_sources" "$base_mirror" "$base_dist" "$components"
          ;;
        esac
      ;;
      ubuntu|ubuntu-old|ubuntu-ports)
        if [ "$base_dist" != "${profile%%-*}" ]; then
            die "Unknown Ubuntu based profile=$profile"
        fi
        pockets=""
        # whether to include the security mirror
        has_security="yes"
        case "$profile" in
          *-security)
            pockets="security"
          ;;
          *-updates)
            pockets="security updates"
          ;;
          *-backports)
            pockets="security updates backports"
          ;;
          *-proposed)
            pockets="security updates proposed"
          ;;
          *)
            if [ "$base_dist" != "$profile" ]; then
                die "Unknown Ubuntu pocket based profile=$profile"
            fi
            has_security="no"
          ;;
        esac
        add_output_sources "$with_sources" "$base_mirror" "$base_dist" "$components"
        for pocket in $pockets; do
            add_output_sources "$with_sources" "$base_mirror" "$base_dist-$pocket" "$components"
        done
        if [ "$has_security" = "yes" ]; then
            archive="ubuntu-security"
            mirror="$(get_archive_url "$mirror_map" "$archive")"
            if [ "$mirror" != "$base_mirror" ]; then
                add_output_sources "$with_sources" "$mirror" "$base_dist-security" "$components"
            fi
        fi
      ;;
      *)
        die "Unknown profile for base_archive=$base_archive"
      ;;
    esac
    set_debootstrap_suite "$base_archive" "$base_dist"
    debootstrap_mirror="$base_mirror"
fi

if [ -n "$suite" ]; then
    if [ -z "$mirror" ]; then
        if [ -z "$archive" ]; then
            archive="$(guess_dist_arch_archive "$suite" "$arch")"
        fi
        mirror="$(get_archive_url "$mirror_map" "$archive")"
    fi
    add_output_sources "$with_sources" "$mirror" "$suite" "$components"
    for pocket in $pockets; do
        add_output_sources "$with_sources" "$mirror" "$suite-$pocket" "$components"
    done
    # NB: archive might be empty; best effort to try to guess it
    if [ -z "$archive" ]; then
        archive="$(guess_dist_arch_archive "$suite" "$arch")" 2>/dev/null || true
    fi
    set_debootstrap_suite "$archive" "$suite"
    debootstrap_mirror="$mirror"
fi

case $1 in
  apt-sources)
    echo "$sources"
  ;;
  debootstrap-mirror)
    echo "$debootstrap_mirror"
  ;;
  debootstrap-suite)
    echo "$debootstrap_suite"
  ;;
  *)
    usage
    exit 1
  ;;
esac

