#!/bin/bash
# Author:

WORK_DIR=/var/tmp/install_catchpoint
THIRDPARTY_REPO_NAME=catchpoint-{X}-repo-third-party
REPO_NAME=catchpoint-rhel-repo
EULA=${WORK_DIR}/EULA.txt
REPO_FILENAME=catchpoint_{X}.repo
REPO_FILE=${WORK_DIR}/${REPO_FILENAME}
AGENT_CONF_FILE=/etc/catchpoint.d/agent.conf


OS_DIST=$(source /etc/os-release;echo $ID)
#use only the Major versionn
OS_VER=$(source /etc/os-release;echo $(printf %.1s "$VERSION_ID"))


catchpoint_user=serveruser
catchpoint_group=cp
[[ -e $AGENT_CONF_FILE ]] && . $AGENT_CONF_FILE # override with sourced values set in the conf file


usage() {
    info "usage: $0 [--help] [-V|--version] [-a|--accept-license]
                               [-q|--quiet] [-v|--verbose] [-d|--dry-run]
                               [--enablerepo=<repo>] [RPM file]

  --help                Display usage
  -V, --version         Version of Catchpoint Synthetic Agent that going to install
  -a, --accept-license  Accept EULA and suppress EULA popup
  -q, --quiet           Suppress outputs
  -v, --verbose         Show verbose info during installation
  -d, --dry-run         Display installation command without actually running it
  --enablerepo=<repo>   Enable extra YUM repos during installation
  RPM file              Install the supplied local RPM package
"
}

verbose() {
    [ $quiet -eq 1 ] && return || :
    [ $show_verbose -eq 0 ] && return || :
    echo "> $1"
}

info() {
    [ $quiet -eq 1 ] && return || :
    echo "$1"
}

warn() {
    [ $quiet -eq 1 ] && return || :
    echo "warn: $1" >&2
}

error() {
    [ $quiet -eq 1 ] && return || :
    echo "error: $1" >&2
}

fatal() {
    local exit_code=$1
    local msg="$2"

    cleanup all

    error "$msg"
    exit ${exit_code}
}

is_offline(){ false; }

print_version() {
    local rpm_file=$1
    local version=""
    if [ -n "${rpm_file}" ]; then
        if [ ! -f ${rpm_file} ]; then
            fatal 4 "The supplied RPM file doesn't exist: ${rpm_file}"
        fi
        version=$(rpm -qp --queryformat "%{VERSION}" ${rpm_file})
        info "$version (local)"
    else
        extract_packages
        version=$(yum info ${disable_repo} --enablerepo=$repos SyntheticAgent 2>/dev/null | awk '/^Version/ {print $3}')
        if [ -z "$version" ]; then
            fatal 4 "SyntheticAgent package not found"
        fi
        is_offline &&
            info "$version (offline)" ||
            info "$version (online)"
        cleanup all
    fi
    exit 0
}

extract_packages() {
    need_root
    [ ${is_extracted} -eq 1 ] && return
    rm -fr ${WORK_DIR}
    verbose "extract_package: mkdir ${WORK_DIR}"
    if ! mkdir ${WORK_DIR} > /dev/null 2>&1; then
        fatal 5 "Failed to create ${WORK_DIR}"
    fi
    verbose "extract_package: extract tarball to ${WORK_DIR}"
    if ! sed -n ${archive_begin}',$p' $0 | tar xJ -C ${WORK_DIR} > /dev/null 2>&1; then
        fatal 5 "Failed to extract packages"
    fi
    is_extracted=1
}

cleanup() {
    verbose "cleanup: rm -fr ${WORK_DIR}"
    rm -fr ${WORK_DIR}
    if [ "$1" = "all" ]; then
        verbose "cleanup all: rm -f /etc/yum.repos.d/${REPO_FILENAME}"
        rm -f /etc/yum.repos.d/${REPO_FILENAME}
    fi
}

customize_config_entry() {
    conf_message="# For Python compatibility, place double quotes around the values being assigned, if editing manually.\n# It is recommended that you use the install_catchpoint.sh script to modify usernames or groups, to properly chown the files."
    filename=$AGENT_CONF_FILE
    local key=$1
    local default=$2
    local current=$3
    local message=$4
    local extra_message=$5

    while true; do
        local now=$default
        local newnow=""

        if [ ! -z "$current" ]
        then
            now=$current
        else
            current=$default
        fi

        info
        [ -z "$extra_message" ] || info "$extra_message"
        info "Current $message: $now (Press ENTER to keep)"
        read -p "Enter $message: " newnow
        newnow=${newnow:-$now}

        type="user"
        check_type="passwd"
        if [[ $key == "catchpoint_group" ]]
        then
            type="group"
            check_type="group"
        fi

        check=$(getent $check_type $newnow > /dev/null 2>&1 || echo "Not Found")
        if [[ ! -z $check ]]
        then
            info 
            read -p "Please confirm you want to create a new $type named '$newnow'? (y/N): " -n 1 -r
            info 
            if [[ $REPLY =~ ^[Yy]$ ]]
            then
                break
            fi
        else
            break
        fi
    done

    if [ "$current" != "$newnow" ]
    then
        if [ "$newnow" == "$default" ] && [ -e $filename ]
        then
            # remove the current customization since we are going back to the default
            sed -i -e "s/^$key=\".*\"//" $filename
            #echo "REMOVED $key=\"$newnow\""

            # if the agent.conf is empty (except for default comment), remove it
            if [ ! -s $filename ] || [ $(cat $filename | tr -d '[[:space:]]') == $(echo $conf_message  | tr -d '[[:space:]]') ]
            then
                rm $filename
                #echo "REMOVED $filename"
            fi 
        else
            #create config file if it doesn't exist
            if [ ! -e $filename ]; then
                agent_conf_dir=$(dirname $filename)
                mkdir -p $agent_conf_dir
                chown root:root $agent_conf_dir
                chmod 0755 $agent_conf_dir
                echo $conf_message >> $filename
                chown root:root $filename
                chmod 0644 $filename
                #echo "CREATED $filename"  
            fi

            grep=$(grep "$key=" $filename)
            if [ -z "$grep" ]
            then
                echo "$key=\"$newnow\"" >> $filename # quotes for python compatibility
                #echo "ADDED $key=\"$newnow\""  
            else
                # replace the current customization since we are going back to the default
                sed -i -e "s/^$key=\".*\"/$key=\"$newnow\"/" $filename
                #echo "REPLACED $key=\"$newnow\""  
            fi
        fi
    else
        : #echo "UNCHANGED $key"  
    fi
}

recursive_chown_with_new_user() {

    if [ $dryrun -eq 1 ]
    then
        info "dryrun"
        # NOTE: Dry run does not currently include exercising the custom user or group functionality.
        return 1
    fi  

    local path=$1
    local old_user=$2
    local new_user=$3
    local old_group=$4
    local new_group=$5
    local message=$6
    local extra_message=$7

    if [[ ! -d "$path" ]]
    then
        return
    fi

    # don't run commands if the user or group don't exist, which might be true on first install
    # for serveruser/cp
    local check_user=$(getent passwd $old_user > /dev/null 2>&1 && echo "Found")
    local check_group=$(getent group $old_group > /dev/null 2>&1 && echo "Found")
    if [[ -z "$check_user" && -z "$check_group" ]]
    then
            return
    fi

    local needs_chown=""
    if [[ ! -z "$check_user" ]] 
    then
        needs_chown=$(find $path -user $old_user -print -quit)
    fi

    if [[ -z "$needs_chown" ]]
    then
        if [[  ! -z "$check_group" ]]
        then
            needs_chown=$(find $path -group $old_group -print -quit)
        fi

        if [[ -z "$needs_chown" ]]
        then
            return
        fi
    fi

    info
    info "Checking $path for files that need owner and/or group changed."


    if [[ ! -z "$message" && $quiet -ne 1 ]]
    then
        info ""

        if [[ ! -z "$extra_message" ]]
        then
            info "$extra_message"
        fi
        read -p "$message(y/N): " -n 1 -r

        info ""
        if [[ ! $REPLY =~ ^[Yy]$ ]]
        then
            return 1
        fi
    fi

    if [ $quiet -eq 1 ]
    then
        if [[ ! -z "$check_user" && ! -z "$check_group" ]]
        then
            find $path -user $old_user -group $old_group -exec chown -h $new_user:$new_group {} \;
        fi
        if [[ ! -z "$check_user" ]]
        then
            find $path -user $old_user -exec chown -h $new_user {} \;
        fi
        if [[ ! -z "$check_group" ]]
        then
            find $path -group $old_group -exec chgrp -h $new_group {} \;
        fi
    else
        if [[ ! -z "$check_user" && ! -z "$check_group" ]]
        then
            find $path -user $old_user -group $old_group -exec echo "Replacing user & group for {}" \; -exec chown -h $new_user:$new_group {} \;
        fi
        if [[ ! -z "$check_user" ]]
        then
            find $path -user $old_user -exec echo "Replacing user for {}" \; -exec chown -h $new_user {} \;
        fi
        if [[ ! -z "$check_group" ]]
        then
            find $path -group $old_group -exec echo "Replacing group for {}" \; -exec chgrp -h $new_group {} \;
        fi
    fi
}

bannerize_text() {
    printf "*  %-62s  *\n" "$1"
}

bannerize_line() {
    printf "********************************************************************\n" "$1"
}

customize_user_group() {

    [ $quiet -eq 1 ] && return || : # customize directly with /etc/catchpoint.d/agent.conf if needed in quiet

    if [ $dryrun -eq 1 ]
    then
        info "NOTE: Dry run does not currently include exercising the custom user or group functionality."
        return 1
    fi  
    while true; do
        default_user=serveruser
        default_group=cp
        default_browser_user=serveruser

        old_user=$catchpoint_user
        old_browser_user=$catchpoint_browser_user
        old_group=$catchpoint_group

        # Reread the conf file without defaults so we only have whats already there if anything
        catchpoint_user="" # serveruser
        catchpoint_browser_user="" # serveruser
        catchpoint_browser_group="" # cp

        [[ -e $AGENT_CONF_FILE ]] && . $AGENT_CONF_FILE

        bannerize_line
        bannerize_text "Catchpoint Agent Installer"
        bannerize_text ""
        bannerize_text "Catchpoint Agent requires a Username and Group to run as."
        bannerize_text "The default username is 'serveruser' and group is 'cp' but can"
        bannerize_text "be changed below. If you provide a username or group that"
        bannerize_text "currently does not exist on the system, it will be created for"
        bannerize_text "you. Agent will use the username and group provided to run its"
        bannerize_text "services, cronjobs, and other processes."
        bannerize_line

        customize_config_entry catchpoint_user "$default_user" "$catchpoint_user" "Username"
        # TODO: customize_config_entry catchpoint_browser_user "$default_browser_user" "$catchpoint_browser_user" "Browser Username" "(Recommended for Browser tests to run under a different username for security.)"
        customize_config_entry catchpoint_group "$default_group" "$catchpoint_group" "Group"

        # Reread the conf file with defaults so we pick up any changes
        catchpoint_user=serveruser
        catchpoint_browser_user=serveruser
        catchpoint_group=cp
        [[ -e $AGENT_CONF_FILE ]] && . $AGENT_CONF_FILE

        info
        info "Confirm the following settings"
        info "Catchpoint Agent Service User: $catchpoint_user"
        # TODO: info "Catchpoint Agent Browser User: $catchpoint_browser_user"
        info "Catchpoint Agent Group       : $catchpoint_group"

        info
        read -p "Please confirm(y/N): " -n 1 -r

        info 
        if [[ $REPLY =~ ^[Yy]$ ]]
        then
            return 0
        fi
    done
}

prompt_eula() {
    [ $quiet -eq 1 ] && return 1 || :
    [ -t 1 ] && plain=0 || plain=1
    which whiptail > /dev/null 2>&1 || plain=1
    if [ ! -f "$EULA" ]; then
        fatal 6 "EULA file not found: $EULA"
    fi
    local eula=$(cat $EULA)
    if [ $plain -eq 0 ]; then
        whiptail --yesno "$eula" --clear --scrolltext --backtitle "SyntheticAgent" --title "Catchpoint Systems, Inc." 0 0
    else
        info "$eula"
        read -p "I have read and accept the terms of EULA. (y/n) " -n 1;
        test "${REPLY,,}" = "y"
    fi
}

pre_check() {
    verbose "pre_check: check hostname length"
    local hname=$(hostname)
    if [ ${#hname} -ge 65 ]; then
        error "hostname is greater than 64 characters"
        return 1
    fi
    return 0
}

post_check() {
    local warn_bits=0
    verbose "post_check: check time synchronization"
    if ! systemctl is-active chronyd --quiet && ! systemctl is-active ntpd --quiet; then
        warn_bits=$(($warn_bits | 0x01))
    fi

    verbose "post_check: check timezone"
    if [ "$(date +%Z)" != "UTC" ]; then
        warn_bits=$(($warn_bits | 0x02))
    fi

    verbose "post_check: check firewall for https service or 443/tcp" 
    if ! firewall-cmd --list-services | grep -q https && ! firewall-cmd --list-ports | grep -q '443/tcp'; then
        warn_bits=$(($warn_bits | 0x04))
    fi

    verbose "post_check: check SELinux status" 
    selinux_status="$(getenforce 2>/dev/null)"
    if [ "${selinux_status,,}" = "enforcing" ]; then
        warn_bits=$(($warn_bits | 0x08))
    fi

    [ $((${warn_bits} & 0xff)) -ne 0 ] && info "**************************************** Important Information ****************************************" || :
    [ $((${warn_bits} & 0x01)) -ne 0 ] && info "* Enable time synchronization: systemctl enable <chronyd|ntpd|systemd-timesyncd>                      *" || :
    [ $((${warn_bits} & 0x02)) -ne 0 ] && info "* Timezone is not UTC: timedatectl set-timezone UTC                                                   *" || :
    [ $((${warn_bits} & 0x0c)) -ne 0 ] && info "* To allow users to change Network Settings and Troubleshooting from Catchpoint Portal:               *" || :
    [ $((${warn_bits} & 0x04)) -ne 0 ] && info "*   Open Firewall to allow HTTPS connection:                                                          *" || :
    [ $((${warn_bits} & 0x04)) -ne 0 ] && info "*     $> catchpoint os --enable-port=443/tcp                                                          *" || :
    [ $((${warn_bits} & 0x08)) -ne 0 ] && info "*   Set SELinux to Permissive state:                                                                  *" || :
    [ $((${warn_bits} & 0x08)) -ne 0 ] && info "*     $> catchpoint os --set-selinux=permissiveÂ                                                       *" || :
    [ $((${warn_bits} & 0xff)) -ne 0 ] && info "*******************************************************************************************************" || :
}

prepare_installation() {
    verbose "prepare_installation: cp $REPO_FILE /etc/yum.repos.d/"
    rm -f /etc/yum.repos.d/catchpoint*.repo
    cp $REPO_FILE /etc/yum.repos.d/ > /dev/null 2>&1
    if [ $? != 0 ]; then
        cleanup all
        fatal 9 "Failed to copy repo file to /etc/yum.repos.d/"
    fi
}

need_root() {
    if [ $(id -u) -ne 0 ]; then
        error "This command must be run as root"
        exit 2
    fi
}

accepted=0
quiet=0
show_verbose=0
dryrun=0
extra_repos=""
rpm_file=""
archive_begin=$(awk '/^--ARCHIVE BEGIN--$/ {print NR + 1;exit;}' $0)
is_extracted=0

args=`getopt -o Vaqvd --long help,version,accept-license,quiet,verbose,dry-run,enablerepo \
    -n 'invalid arguments' -- "$@"`
if [ $? -ne 0 ] ; then
    exit 1
fi
eval set -- "$args"

while true; do
    case "$1" in
        --help)              usage; exit 0;;
        -V|--version)        show_version=1; shift ;;
        -a|--accept-license) accepted=1;     shift ;;
        -q|--quiet)          quiet=1;        shift ;;
        -v|--verbose)        show_verbose=1; shift ;;
        -d|--dry-run)        dryrun=1;       shift ;;
        --enablerepo)        extra_repos=$2; shift 2 ;;
        --) shift; break ;;
        *) error "Internal error!"; exit 2 ;;
    esac
done

rpm_file=$1

shift || :
if [ $# -ne 0 ]; then
    error "Invalid or Extra arguments will be ignored: $@"
fi

if [ "${OS_DIST}${OS_VER}" = "rhel7" ]; then 
    repos="rhel-7-server-rpms"
    disable_repo="--disablerepo=*"
elif [ "${OS_DIST}${OS_VER}" = "rhel8" ]; then 
    # take all the repo from redhat8 distribution
    repos=""
    disable_repo="" # don't disable standard repo in readhat8
elif [ "${OS_DIST}${OS_VER}" = "centos7" ]; then 
    repos="base,updates"
    disable_repo="--disablerepo=*"
elif [ "${OS_DIST}" = "amzn" ];    then
    if [ "${OS_VER}" = "1" ]; then
        fatal 3 "Amazon Linux AMI is not supported, we only support Amazon Linux 2."
    fi
    repos="amzn2-core"
    disable_repo="--disablerepo=*"
else
    fatal 3 "This platform is not supported."
fi

if [ "${OS_DIST}${OS_VER}" = "rhel7" -o "${OS_DIST}${OS_VER}" = "centos7" -o "${OS_DIST}" = "amzn" ]; then
    enterprise_linux_version="rhel7"
elif [ "${OS_DIST}${OS_VER}" = "rhel8" ]; then
    enterprise_linux_version="rhel8"
else
    fatal 3 "This platform is not supported."
fi

REPO_FILENAME=$(echo "$REPO_FILENAME" | sed "s/{X}/$enterprise_linux_version/")
REPO_FILE=$(echo "$REPO_FILE" | sed "s/{X}/$enterprise_linux_version/")
THIRDPARTY_REPO_NAME=$(echo "$THIRDPARTY_REPO_NAME" | sed "s/{X}/$enterprise_linux_version/")

[ -n "$extra_repos" ] && repos="${extra_repos},${repos}" || :
repos=$(echo "${repos},${REPO_NAME},${THIRDPARTY_REPO_NAME}" | sed -r -e "s/^,+//g" -e "s/,+$//g" -e "s/,{2,}/,/g")
if [ "${OS_DIST}${OS_VER}" = "rhel8" ]; then 
    verbose "YUM repos that are being used: $repos and ${OS_DIST}${OS_VER} repository"
else
    verbose "YUM repos that are being used: $repos"
fi

if [ ${show_version:-0} -eq 1 ]; then
    print_version ${rpm_file}
fi

extract_packages

if [ $accepted -ne 1 ]; then
    if ! prompt_eula; then
        fatal 7 "EULA is not accepted, SyntheticAgent installation terminated."
    fi
fi

if ! customize_user_group; then
    fatal 9 "Custom user/group invalid, SyntheticAgent installation terminated."
fi

if ! pre_check; then
    fatal 8 "Prerequisites are not met, SyntheticAgent installation terminated."
fi

prepare_installation

if [ -z "${rpm_file}" ]; then
    rpm_package=SyntheticAgent
else
    if [ ! -f "${rpm_file}" ]; then
        fatal 10 "The supplied RPM file doesn't exist: ${rpm_file}"
    fi
    rpm_package=${rpm_file}
fi

if [ $dryrun -eq 1 ]; then
    echo "[Dry run] yum install -y ${disable_repo} --enablerepo=$repos ${rpm_package}"
else
    if [ $quiet -eq 1 ]; then
        yum install  -y ${disable_repo} --enablerepo=$repos ${rpm_package} >/dev/null 2>&1
    else
        yum install  -y ${disable_repo} --enablerepo=$repos ${rpm_package}
    fi
    if [ $? -ne 0 ]; then
        fatal 11 "Installation failed"
    fi
fi

# Change things that aren't currently handled by other means
if [[ "$old_user" != "$catchpoint_user" || "$old_group" != "$catchpoint_group" ]]
then
    recursive_chown_with_new_user "/var/run/catchpoint" "$old_user" "$catchpoint_user" "$old_group" "$catchpoint_group"
    recursive_chown_with_new_user "/opt/3genlabs/registry" "$old_user" "$catchpoint_user" "$old_group" "$catchpoint_group"
    recursive_chown_with_new_user "/opt/3genlabs/network" "$old_user" "$catchpoint_user" "$old_group" "$catchpoint_group"
    recursive_chown_with_new_user "/var/log/catchpoint/" "$old_user" "$catchpoint_user" "$old_group" "$catchpoint_group"
    recursive_chown_with_new_user "/opt/3genlabs/hawk/syntheticnode/service/chrome" "$old_user" "$catchpoint_user" "$old_group" "$catchpoint_group"

    if [ $quiet -ne 1 ]
    then
        recursive_chown_with_new_user "/opt/3genlabs/hawk/syntheticnode/service/shellmonitor/sandbox" "$old_user" "$catchpoint_user" "$old_group" "$catchpoint_group" "Confirm" "Change owner/group on any existing Custom Monitor sandbox scripts? Please double check for security."
    fi
fi

is_offline && cleanup all || cleanup

post_check

exit 0

--ARCHIVE BEGIN--
ý7zXZ  æÖ´F !   t/å£àOÿ�] 1˜JîÅ0N« ç²ù(TG¹÷°(ÒÐ4HC¦Ý¬|çöÕ-Õù]Þ€¬=X³Hë†I˜tbC¯>ƒE�=¹èÒÂ
µ»§1çbžÂ EªÓ¥¢�iŒXROB¾Î÷Ÿ|ýô<ÚÏŒÿZù:iç¥­.c£×E"Q‡_úKZô�õÖž^Ž(¡§S†ÈVEògôæÎú…j]#ôØã=‚ñ[IJÔ‘¬_`Q £/T4H$‘X0Pe“Q„Ç'|IœwSP�¼=Þ½ˆÈÄ/ÎÏPu¶==[œ´å?YäûÏV¨’°É�%5<Îf”.Ö}|Çì¨%>öd!ì5@Uª‰8Ýâé ù·§˜9"—ªûäÛ��þÀáe©n8kùD?kl§Ûö==èþ”A˜‚©.¡5ëÚÂó®HHˆ+G¶ÿ1�·QL…=IÀ§˜t2™™ø-¿W§A%¡Akôû^Þò=A€ÉöªÃAÄ¼®Ú‚q×!_/}âïüpšSA/‚l;ùQNÍsç_aŠ^uh4®ÜUÊjÄmô‘k�md§+nP(¶´BV	è®¤®MõÑ	ÒdMo�—¥ì—DÊ•.†,ö“#Gå³}õsŠ†¯ùû@Kž<óÌ•ù®ñŽITù‹�©7iôcöÝôOÍ¡½÷e·T²¿@y|ØXÕõ&Ï×túføC«á;U<ê6LÔÊ¯Íi]Œt9Ö¡©�+µª§öµ÷iMwñ¶³<VWê¶å›¦ñªý´½iåÍ/±!bt‰,hnQ¦5Æ‹CS ÑQ�q’{ÃÅ?;raÀëÕ[[cäo—³ùD6u×£^þ�ï‘Çâ¿‹¥“F©»&xª”ÉÜ"¹Ðö”rÝ65Vq¬?ß=w²q@iê…VaÀ‚—×�“Aó8’Üè]º”âÙ*Ë–ž “#©ùsº�*3µþ}¡¤[ºPÜßøékB«QâŠ¡{Ú^Õf(K�G*­ÙýåN'÷fÊå|ÌŒà$Ì·Ù*õ\«e“>ž¡¦rŽ½*.8í›zX,õ]ñ>£­¯Žº[ÔuX&XØ¥}+Aã­Û–¡#Š¹#2�9É€sò…ƒ™‡µ£›„þ¿± D(-ÃÐ|=YÍAk~S„q‘ =7LŒ@¿[SëÅùÝ‚„¿é�™¾Ì¿b°™›`ÅCr¯ïVÆ£�«{ö³¦aïm‹Î7[ÿ€-¼úXÌšYµì-‰´…5	¤¸w5¼P–ž¦Åµ
ò� “$�ÖÃÛˆßíá@†7Ë²²{×ÁÆQ{({w¥ª
‹…mr–çU/éÙØÔ€1K=”dŸK=7ú³äh=�ÈL0‘!Î­	æ™ÖnÞº»ä7w]¡%õ
<Á$€
2ç§ùV­S7/î²iç'yTBÛi3`ö—R`ÑÅŸK2ì©H—ºþ‡œ�P·†·ÍP²2¹!º}Ð\ðVÃã˜¡OÞ5ÎŸKìeƒ£ÇÅ0Ù<:+½aÀ4!¤{-*:˜ÿ»LïLÞ¾Às†t¤“+ 'ÿ×�¡DfH™”'Ä"œ)@ÄP@Ø¡k:ìˆ€$oŠÕ^^´=áóøy5m>vw“=#*ä®é›DcIœgî�9ÒšÀ,–Ò]t Xn/rÅð0P Šb¸ƒ‚T›P§[#ó”àÕ�“ÙøÎ¹;òÜ°´cÃ-öÇ~‹|Âb¢‚äfÛöŽøŸÉ]¨ÎéœþæR‹G´˜m¡[D?n¾l„U¬ëa¨£¾Ž©Ùî"ç1¢]ÎøZÕb¤¼&¨*sRO®q4nôd‘véÄBœ÷Ãt/Õt!‘�±j	_¢’Sî%Îø~XŽ”�ŠS„UŽ)ºu_–¨n2,ÓÔ7„‡…•Ùo- y{õÚ÷p¨Ã« š³poàõ°&/M%ß@ÐÊÙÇ$P”üJ³'Œ™ˆI¥†¬ $"Ï­Ê8´§?ðšF´Hîy=@®g'¡RŸÐ¬(Os©m¤
9jÆpÂlr.öí°8b†»íÈ‚ßqf9€èJL\Àt ÉÛ'$mÐ¦ÎðîZÚ¨²:š�=„Qo›‡‚>Np�÷ÇÏf8ØÎpËNZudŽüc/IgÿéQ.œÑC@ÍÕÝ±	pxŽù	•b�Ô¿IPs€õÓÂáYf¶K§«S	í˜×>§+�‰ŽÎJ:^4%€A•‚)ï—6æ#»ò�þz xÞôÏ]ÑÞZ—þRìÅ‡Dxg�^m„>xä­iXQ0‚¢”Œ£žG}q¯Vð·°–¿4Ð¥õÀâÔþNAêðr}®"³Ÿ£“ÈE	ÏF¿CÐ¹[†¦Ü*KóÞÈU4Ô­Ò½¨9êÑÙ]wØ!„/]7Í-ÔJ>º\`Yœ|¯¥¦¦�¨w¤DH9Þoákj”m~(Žªò¸$âÙÄ,ÍOßFðù-ÈäJ�LûÝœÞD·X§Ü@ö“‹Öõ1rŸS[kNÌSÐ,dy@´hD÷Û¨h�HB@�Dioèò Ž×ØêÍ°V9#/Ÿ~™"|ä“Îø�»�(œqÂïm˜�±Ú¼¦_}í;Îtðã­×°€^‚–Iã5&�åžŒÐ1€ÿ¸‘&”>žº€¾7ÈFq¦w€¶ý;¦8Å³SZG­«ŽE2¨«Å–Ïóºóhy(Ç|wQ>\0sÆÉŠ)O×‡ûø­;¡›}Kº&|¼ÉPÙ–eòÒµ8åÉ¸ÊAê/exÒ7þ×5Áõ×Às@5áV^/¬sE˜ A7fA€Xä %ó†¯Wï} r³úÇº8ÒøL¡�*ç±Î”âæŽKNEkL˜DdþÊÄÐ¶›p<îÐ&Dg'È=NÎªÆƒ_Bo}_˜Év¾-Á9Nµ²xIBËçm g[Ã°'4fíýVh˜óÇ'�»?°-• Î��+ê5ñGÑæY¶“ãšBÖ\)âXtáÓs)fÜÈ1ËƒeÊ%àùc…‡ŠÜ,GoNËëG£°�]ŸZ0`´ø9f³ vÒß-gê¤¼L|-Ã�YGT§5÷Zî �‘9«´©'ûÉìÅØŒwnV_[Ñ
‹[þuÃX½Õiwçêmf,\<W¿!AèÄºÚÇ5
óÒ¯D}�Ì,³H	R,jâ™#Ý~¾:TÒ§Ç»>N¦>§#£¨ƒfWöŽµÅÚ�¼ÆÀŠÀÃ£6ó�àÆ)‡<’k×M±¤¾d˜ÀNŽ}PF”¢ÜýùŽ1KÍ’MùK¬wQv9Ï¿u!ÓÓ½ð��Þ„&†ôø‡lG:+4§˜A"_û§fJÏiLí¼Ä)-ÄN‡50;ŒàÖ•ÖÔºÿ¿Ÿ.KHQÔ×f‘°%RRR‡ç|ˆÞ‰µZù·Â1zÙÞÄ“c¯îSKžzH±M4øJÜB¬eàXÐOaÇ p˜o#¦¶âù6��*C¼iÚºbwú²��c<ñûf!u )>S,h.t+ °ÏŒEÐBÁ*8×Ñö¾ßqa÷fôPÈÄ­Rî1SÀýêg38Œò…EH[0K_r:ãÎµ¾üBtsc1¹*/RQ•3ñ›?åèþ¶€`e3Šµ‚WßEÖ‰å­¯1—–´AÃýŽ¿@93¢ÉhÎUœÞëú¿¡ä®ß±âa²v¾õ¤‹»³„q__áŠ/p:¾”s1’dÍ”~·™F?zTýr;¹Íó
LÛ=øuRêvÐq¥wæó¤®Ìh0±®ò§2z×>YÌW)$ô­,
7öÊ*F-†YlÖ€ŠŠG¤œòÇÑ6ýS,4S¯©ì‹U'^?§�Ipý1�~ŒˆïÊS>µ¶ˆ F^Â´Âj%äH÷u|ÀÕª£GÜ¹»>C@C9ìüUÒ°‹¬ŒŽ}¡·Y'“Äß¸¶ÕJÆ×ðE§›µ_»/y7î# ìZpq‹qVGê‰·0¸ ›¡áÄBÌh5"$x‰í›L[@µî}Æ['l÷ï¹5)P7vWIq¡;F.QöBÈ:£‡.Ý6°Uè¯á\ ÊÜœ;Præxb.èðöÁµŠÉ.Iú
l=¹+ØÇ[¢Ü bèü¯,ùCM–W¨QÌdäÂ­kO'å_Q™æ}¦ ’›·à½åÍ8kZ@„¸“M¡U9ìßÄ�¬‚¨™M74¡~Ý	8Å·X[z"FwdhEŠÙ•3µò¿¦Óü\åöö[k& îD,�i>w@qý~3£ä¼Ø®œU®"3=û¿®p˜ñêY3».¡CÐ>ÄnþJÖ¡9Œ¡ûÉEgqÙ>,;bÄŠÅxrÞ9õnI*­£tôt«ÚAÔ]êœo:Œ3c|0¶rÊn˜•Ï·s­{ÄA0ÚoÄ�)ßE4ÂJ¿5�/ûÀ`®J¤Ø¶±äVócÐa ùºì5¨ó 
FÐ¸šVª×î~îaáå6Vvh,˜x»¤ã×¤„:dÉUƒ¿k¸ïà!½ˆÔîqSßËaœ3´7Q®wS?UïlZ½™#(NñE)"T XônCxƒÊ�>€÷¯ýÍrÝøÇ«ŽÈˆ&‚ˆU·z;w<'tP´‡²µT”Kž¤ÁK÷j¸ŸŽKÐñVg±.æ:bŸÝÜÝì+‹ìÈë_¥µ|ì}Fæ­ràf~ohÄ†¡ÈF{›Ñ—ö¤Ï¥œ§&üËºj]àíàT�Üêq:òeAŒÞ‡}o¨V1¯e•±|­jýÐã¬×¤³nâRÒT’$Zéú4Tei÷Pv{ï‘a©ÎÏrÅ–Ž'Nd¥Éf÷ðÅf•…;_Ž(¤O•ý®wýî¶·ÊÁª$Ï‹™îdXÎQ2¼—oûÐ�ƒÞý‚O\Š˜	=ûÊQD£´�3StUÁ]Aç™ü7Å:Dà´»#ûæ  ¿>¿x6‰)« ¬€  aŒÁ±Ägû    YZ