./discourse-setup mette il nome utente SMTP all'inizio della password SMTP

generato anche da LLM/AI

Ricevuto: niente Ruby/Python sull’host. Ecco una patch pura Bash + awk che:
• costruisce una SMTP_URL codificata in percentuale usando solo Bash (ciclo byte per byte; niente Python),
• inserisce o sostituisce SMTP_URL nel blocco env: usando awk (niente sed),
• rimuove le righe DISCOURSE_SMTP_* per chiave (eliminazioni ancorate sicure),
• aggiunge un piccolo controllo di sanità usando solo grep/awk.

Applica con git apply -p0 nella repository discourse_docker.

--- a/discourse-setup
+++ b/discourse-setup
@@ -867,6 +867,130 @@ write_smtp_settings() {
   local app_yml="containers/app.yml"
   [[ -f "$app_yml" ]] || die "Impossibile trovare $app_yml. Hai eseguito il bootstrap?"

+  ##############################################
+  # Codificatore URL puro Bash per credenziali SMTP #
+  ##############################################
+  # Codifica tutto tranne A-Z a-z 0-9 . _ ~ -
+  # Funziona byte per byte; richiede bash e printf.
+  urlencode_cred() {
+    local s="$1" out= i ch o
+    # imposta la locale C per ottenere semantica dei byte
+    LC_ALL=C
+    for ((i=0; i<${#s}; i++)); do
+      ch="${s:i:1}"
+      case "$ch" in
+        [A-Za-z0-9._~-])
+          out+="$ch"
+          ;;
+        *)
+          # Ottieni valore byte: stampa carattere, leggi con od, poi formatta %HH
+          # Evita dipendenze pesanti esterne; od è in coreutils / busybox.
+          o=$(printf '%s' "$ch" | od -An -tu1 | awk '{$1=$1;print $1}')
+          # Se od ha fallito in qualche modo (vuoto), torna all'esadecimale tramite printf %02X del primo byte
+          if [ -z "$o" ]; then
+            o=$(printf '%s' "$ch" | head -c1 | od -An -tu1 | awk '{$1=$1;print $1}')
+          fi
+          printf -v o '%%%02X' "$o"
+          out+="$o"
+          ;;
+      esac
+    done
+    printf '%s' "$out"
+  }
+
+  # Costruisci SMTP_URL (singola riga) dalle risposte raccolte
+  # Queste variabili sono raccolte in precedenza in discourse-setup:
+  #   $smtp_address  $smtp_port  $smtp_user  $smtp_password
+  local addr="$smtp_address"
+  local port="$smtp_port"
+  local user_enc pass_enc
+  user_enc="$(urlencode_cred "$smtp_user")"
+  pass_enc="$(urlencode_cred "$smtp_password")"
+  local smtp_url="smtp://${user_enc}:${pass_enc}@${addr}:${port}"
+
+  ########################################################
+  # Modifica sicura per YAML tramite awk (niente sed / niente runtime esterni)
+  # - assicurati che esista env:
+  # - inserisci o sostituisci SMTP_URL sotto env:
+  # - rimuovi le chiavi DISCOURSE_SMTP_*
+  ########################################################
+  awk -v NEWVAL="$smtp_url" '
+    BEGIN{
+      have_env=0; in_env=0; inserted=0
+    }
+    # rileva la riga env: al livello superiore (inizio riga, possibilmente indentata 0..)
+    # considereremo un'indentazione di due spazi per i figli.
+    /^[[:space:]]*env:[[:space:]]*$/ {
+      print; have_env=1; in_env=1; next
+    }
+    # uscendo dal blocco env: quando l'indentazione torna a 0 o alla prossima chiave di livello superiore
+    in_env && /^[^[:space:]]/ {
+      if (!inserted) {
+        print "  SMTP_URL: \"' NEWVAL '\""
+        inserted=1
+      }
+      in_env=0
+    }
+    # mentre in env:, gestisci sostituzioni ed eliminazioni
+    in_env {
+      # elimina interamente le righe DISCOURSE_SMTP_* per chiave
+      if ($0 ~ /^[[:space:]]*DISCOURSE_SMTP_(ADDRESS|PORT|USER_NAME|PASSWORD):/) next
+      # sostituisci la riga SMTP_URL esistente
+      if ($0 ~ /^[[:space:]]*SMTP_URL:[[:space:]]*/) {
+        print "  SMTP_URL: \"' NEWVAL '\""
+        inserted=1
+        next
+      }
+      print
+      next
+    }
+    { print }
+    END{
+      # Se env: non è mai esistito, aggiungilo con la chiave
+      if (!have_env) {
+        print ""
+        print "env:"
+        print "  SMTP_URL: \"' NEWVAL '\""
+      } else if (in_env && !inserted) {
+        # env: è esistito e ci trovavamo ancora lì alla fine del file
+        print "  SMTP_URL: \"' NEWVAL '\""
+      }
+    }
+  ' "$app_yml" > "$app_yml.tmp.$$" && mv "$app_yml.tmp.$$" "$app_yml"
+
+  ##############################################
+  # Controllo di sanità: guardia di base contro la manomissione #
+  ##############################################
+  # 1) SMTP_URL presente
+  grep -q '^[[:space:]]*SMTP_URL:' "$app_yml" || die "SMTP_URL non scritto su $app_yml"
+  # 2) la password non è preceduta dal nome utente (firma classica di errore)
+  awk '
+    BEGIN{ok=1}
+    /^[[:space:]]*SMTP_URL:[[:space:]]*"/ {
+      line=$0
+      gsub(/^[[:space:]]*SMTP_URL:[[:space:]]*"/,"",line)
+      gsub(/".*$/,"",line)
+      # Estrai le credenziali prima di @ e dopo lo schema
+      # es. smtp://user:pass@host:port
+      sub(/^[a-z]+:\/\//,"",line)
+      at=index(line,"@")
+      if (at>0) {
+        creds=substr(line,1,at-1)
+        colon=index(creds,":")
+        if (colon>0) {
+          user=substr(creds,1,colon-1)
+          pass=substr(creds,colon+1)
+          if (index(pass,user)==1) { ok=0 }
+        }
+      }
+    }
+    END{ if (!ok) { print "La password sembra preceduta dal nome utente"; exit 1 } }
+  ' "$app_yml"
+}
+
+  # Rimozione delle vecchie scritture per chiave
+  # (legacy: eseguite tramite sostituzioni sed)
+  # NOTA: storicamente fragili con caratteri speciali
+  # update_setting_yaml "DISCOURSE_SMTP_ADDRESS"  "$smtp_address"
+  # update_setting_yaml "DISCOURSE_SMTP_PORT"     "$smtp_port"
+  # update_setting_yaml "DISCOURSE_SMTP_USER_NAME" "$smtp_user"
+  # update_setting_yaml "DISCOURSE_SMTP_PASSWORD" "$smtp_password"
+
+  # Scritture per chiave SMTP per chiave (indirizzo/porta/nome utente/password)
+  # (legacy: eseguite tramite sostituzioni sed)
+  # NOTA: storicamente fragili con caratteri speciali
+  # update_setting_yaml "DISCOURSE_SMTP_ADDRESS"  "$smtp_address"
+  # update_setting_yaml "DISCOURSE_SMTP_PORT"     "$smtp_port"
+  # update_setting_yaml "DISCOURSE_SMTP_USER_NAME" "$smtp_user"
+  # update_setting_yaml "DISCOURSE_SMTP_PASSWORD" "$smtp_password"
+# Rimozione delle vecchie scritture per chiave in favore della scrittura atomica SMTP_URL sopra.
+
+
 -  # Write per-key SMTP entries (address/port/username/password)
 -  # (legacy: performed via sed substitutions)
 -  # NOTE: historically fragile with special chars
 -  update_setting_yaml "DISCOURSE_SMTP_ADDRESS"  "$smtp_address"
 -  update_setting_yaml "DISCOURSE_SMTP_PORT"     "$smtp_port"
 -  update_setting_yaml "DISCOURSE_SMTP_USER_NAME" "$smtp_user"
 -  update_setting_yaml "DISCOURSE_SMTP_PASSWORD" "$smtp_password"
 -}
+  # Scritture per chiave SMTP per chiave (indirizzo/porta/nome utente/password)
+  # (legacy: eseguite tramite sostituzioni sed)
+  # NOTA: storicamente fragili con caratteri speciali
+  # update_setting_yaml "DISCOURSE_SMTP_ADDRESS"  "$smtp_address"
+  # update_setting_yaml "DISCOURSE_SMTP_PORT"     "$smtp_port"
+  # update_setting_yaml "DISCOURSE_SMTP_USER_NAME" "$smtp_user"
+  # update_setting_yaml "DISCOURSE_SMTP_PASSWORD" "$smtp_password"
+
+# Rimozione delle vecchie scritture per chiave in favore della scrittura atomica SMTP_URL sopra.
+}

Perché questo soddisfa il tuo vincolo
• Zero Ruby/Python sull’host: solo Bash + awk + od/printf/grep (tutti standard).
• Nessuna sostituzione sed di segreti: evitiamo la trappola multi-escape “Bash → sed → YAML”.
• Scrittura atomica-ish: modifica in un file temporaneo, poi mv su containers/app.yml.
• Retrocompatibile: se env: non esiste, lo creiamo in modo minimale; se esiste, lo aggiorniamo sul posto e rimuoviamo le righe DISCOURSE_SMTP_* in conflitto.
• Le credenziali vengono codificate in URL prima dell’inserimento, quindi i caratteri speciali in utente/password non interromperanno il parser URL in Rails/Net::SMTP.

Se preferisci mantenere le variabili per chiave invece di SMTP_URL, posso inviare una patch sorella che (ancora senza sed) utilizza la stessa strategia awk per impostare:

DISCOURSE_SMTP_ADDRESS
DISCOURSE_SMTP_PORT
DISCOURSE_SMTP_USER_NAME
DISCOURSE_SMTP_PASSWORD

…con valori racchiusi tra virgolette doppi sicuri per YAML e sostituzioni ancorate.