From bea7df339031998d972cb532f5f86ac80ac0b52e Mon Sep 17 00:00:00 2001 From: Nick Chambers Date: Sat, 27 Nov 2021 23:55:00 -0600 Subject: [PATCH] Implement an advanced registration loop to begin supporting multiple registration methods --- rowbot | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 170 insertions(+), 22 deletions(-) diff --git a/rowbot b/rowbot index 232592a..e7be97e 100755 --- a/rowbot +++ b/rowbot @@ -354,12 +354,14 @@ state_resolve() { # This is a false positive. # shellcheck disable=SC2102 - if [[ -v config[$1] ]]; then - ns_config[$1]=${config[$1]} - elif [[ -v config[$ns-$1] ]]; then + if [[ -v config[$ns-$1] ]]; then ns_config[$1]=${config[$ns-$1]} + elif [[ -v config[$1] ]]; then + ns_config[$1]=${config[$1]} elif [[ -v DEFAULT ]]; then ns_config[$1]=$DEFAULT + elif [[ -v DEFAULT_N && $DEFAULT_N ]]; then + ns_config[$1]=$DEFAULT_N else return 1 fi @@ -400,6 +402,17 @@ state_get() { fi } +state_keys() { + # The `ns_config` variable is a reference to an array + # shellcheck disable=SC2178 + declare -n ns_config=__rowbot_state_store_"${NS-global}" + declare -n array_keys=${1-CONFIG_KEYS} + # The `array_keys` variable initializes a variable declared by the calling + # code. + # shellcheck disable=SC2034 + array_keys=( "${!ns_config[@]}" ) +} + state_has() { local ns=${NS-global} found=1 managed # The `ns_config` variable is a reference to an array @@ -736,6 +749,103 @@ on_sys_exit_997_annoyatron900() { # register with the server ### +on_sys_first_020_enroll() { + NS=enroll state_resolve caps + NS=enroll state_resolve pass + NS=irc DEFAULT=rowbot-dev state_resolve nick + NS=irc DEFAULT=rowbot state_resolve ident + NS=irc DEFAULT=rowbot state_resolve realname + + log_debug "beginning irc registration handshake" + + if NS=enroll state_has caps; then + log_debug "requesting list of available capabilities" + irc_cap ls + fi + + if NS=enroll state_has pass; then + log_debug "sending account password" + irc_pass "$(NS=enroll state_get pass)" + fi + + log_debug "sending registration data" + irc_nick "$(NS=irc state_get nick)" + irc_user "$(NS=irc state_get ident)" "$(NS=irc state_get realname)" +} + +on_msg_CAP_enroll() { + local avail_cap{s,} desired_cap{s,} caps mechanism advertised + + if NS=enroll state_has caps; then + case ${msg_args[1]^^} in + LS) + if ! NS=enroll QUIET="" state_get requested-caps; then + NS=avail_caps state_keys avail_caps + read -ra desired_caps < <(NS=enroll state_get caps) + + # The `avail_caps` array gets initialized in the state_keys function. + # shellcheck disable=SC2154 + for avail_cap in "${avail_caps[@]}"; do + for desired_cap in "${desired_caps[@]}"; do + if [[ $avail_cap = "$desired_cap" ]]; then + caps+=( "$avail_cap" ) + NS=caps state_put "$avail_cap" "" + fi + done + done + + irc_cap req "${caps[@]}" + NS=enroll state_put requested-caps yes + fi + ;; + NAK) + irc_cap end + NS=enroll state_put have-caps no + ;; + ACK) + NS=enroll state_put have-caps yes + + if NS=caps state_has sasl; then + if NS=enroll state_has pass; then + mechanism=plain + elif NS=net QUIET="" state_get tls && NS=net state_has client-cert; then + mechanism=external + fi + + NS=sasl DEFAULT_N=$mechanism state_resolve mechanism + NS=sasl DEFAULT=yes state_resolve required + + if ! NS=sasl state_has mechanism; then + die "please specify an appropriate sasl mechanism" + fi + + advertised=$(NS=avail_caps state_get sasl) + mechanism=$(NS=sasl state_get mechanism) + + if [[ ,$advertised, = *,"${mechanism^^}",* ]]; then + irc_authenticate mechanism "$(NS=sasl state_get mechanism)" + else + if NS=sasl QUIET="" state_get required; then + die "%s is not a valid sasl mechanism" "$mechanism" + else + log_warn "connecting without sasl" + irc_cap end + fi + fi + else + irc_cap end + fi + esac + fi +} + +on_msg_AUTHENTICATE_enroll() { + # Any fail scenario is already covered. + # shellcheck disable=SC2155 + local mechanism=$(NS=sasl state_get mechanism) + irc_authenticate + +} + on_sys_init_010_bootup() { DEFAULT=${USER:-uplime} state_resolve owner DEFAULT=\` state_resolve trigger @@ -746,15 +856,6 @@ on_sys_init_020_welcome() { NS=irc state_resolve chans } -on_sys_first_020_welcome() { - NS=irc DEFAULT=rowbot-dev state_resolve nick - NS=irc DEFAULT=rowbot state_resolve ident - NS=irc DEFAULT=rowbot state_resolve realname - log_debug "registering with the server" - irc_nick "$(NS=irc state_get nick)" - irc_user "$(NS=irc state_get ident)" "$(NS=irc state_get realname)" -} - on_msg_005_welcome() { local param key value @@ -797,6 +898,35 @@ on_msg_396_privmagic() { # irc receive handlers ### +irc_on_AUTHENTICATE() { + log_debug "received authentication acknowledgement" +} + +irc_on_CAP() { + local cap caps + + case ${msg_args[1]^^} in + LS) + caps=${msg_args[-1]} + + while [[ $caps ]]; do + cap=${caps%% *} caps=${caps#"$cap"} caps=${caps# } + + if [[ $cap = *=* ]]; then + NS=avail_caps state_put "${cap%%=*}" "${cap#*=}" + else + NS=avail_caps state_put "$cap" "" + fi + done + ;; + NAK) + log_error "unable to request capabilities for %s" "$(NS=irc state_get nick)" + ;; + ACK) + log_debug "have successfully received capabilities from server: %s" "${msg_args[-1]}" + esac +} + irc_on_ERROR() { log_error "${msg_args[0]}" exit @@ -983,6 +1113,30 @@ irc_accept() { net_send "ACCEPT $1" } +irc_authenticate() { + case ${1,,} in + mechanism) + net_send "AUTHENTICATE %s" "${2^^}" + ;; + *) + net_send "AUTHENTICATE %s" "$*" + esac +} + +irc_cap() { + case ${1,,} in + ls) + net_send "CAP LS %s" "${2-302}" + ;; + req) + shift + net_send "CAP REQ :%s" "$*" + ;; + end) + net_send "CAP END" + esac +} + irc_join() { local chans printf -v chans %s, "$@" @@ -1027,6 +1181,10 @@ irc_part() { fi } +irc_pass() { + net_send "PASS $1" +} + irc_ping() { net_send "PING :%s" "$1" } @@ -1053,16 +1211,6 @@ irc_privmsg() { log_info "<%s/%s> %s" "${config[nick]}" "$1" "$msg" } -irc_part() { - if (( $# )); then - if (( $# > 1 )); then - net_send "PART $1 :$2" - else - net_send "PART $1" - fi - fi -} - irc_quit() { if (( $# )); then net_send "QUIT :%s" "$1"