Operazione Metodo Endpoint Note
Autenticazione POST /api/v1/auth/authenticate-user Body: {username, password}
Lista domini GET /api/v1/settings/sysadmin/domains Bearer token
Utenti dominio POST /api/v1/settings/domain/account-list-search Header x-smartermaildomain
import requests
import json
from datetime import datetime
# --- Configurazione ---
SMARTERMAIL_URL = "https://mail.tuoserver.com" # URL del tuo server SmarterMail
USERNAME = "admin" # Username System Administrator
PASSWORD = "la_tua_password" # Password
def authenticate(server_url: str, username: str, password: str) -> str | None:
"""
Autentica l'utente su SmarterMail e restituisce il token di accesso.
Endpoint: POST /api/v1/auth/authenticate-user
"""
auth_url = f"{server_url.rstrip('/')}/api/v1/auth/authenticate-user"
payload = {
"username": username,
"password": password
}
response = requests.post(auth_url, json=payload, verify=True)
if response.status_code != 200:
print(f"Errore autenticazione: HTTP {response.status_code} - {response.text}")
return None
data = response.json()
# Il token puo' essere in "accessToken" o "access_token"
token = data.get("accessToken") or data.get("access_token")
if not token:
print(f"Token non trovato nella risposta: {data}")
return None
print("Autenticazione riuscita!")
return token
def get_domains_list(server_url: str, token: str) -> list[dict] | None:
"""
Recupera l'elenco completo dei domini.
Endpoint: GET /api/v1/settings/sysadmin/domains
Richiede credenziali System Administrator.
"""
domains_url = f"{server_url.rstrip('/')}/api/v1/settings/sysadmin/domains"
headers = {
"Authorization": f"Bearer {token}"
}
response = requests.get(domains_url, headers=headers, verify=True)
if response.status_code != 200:
print(f"Errore recupero domini: HTTP {response.status_code} - {response.text}")
return None
data = response.json()
# Verifica successo
if data.get("success") is False:
print(f"Errore API: {data.get('message', 'Errore sconosciuto')}")
return None
# I domini possono essere in "data", "results" o "domains"
domains = data.get("data") or data.get("results") or data.get("domains") or []
return domains
def get_domain_users(server_url: str, token: str, domain_name: str, take: int = 10000) -> list[dict] | None:
"""
Recupera l'elenco degli utenti di un dominio.
Endpoint: POST /api/v1/settings/domain/account-list-search
Usa l'header x-smartermaildomain per specificare il dominio target.
searchFlags=["users"] per ottenere solo utenti (esclusi alias e mailing list).
"""
users_url = f"{server_url.rstrip('/')}/api/v1/settings/domain/account-list-search"
headers = {
"Authorization": f"Bearer {token}",
"x-smartermaildomain": domain_name,
"Content-Type": "application/json"
}
# Body della richiesta - stesso formato usato in SmarterMailApiService.cs
payload = {
"skip": 0,
"take": take,
"search": "",
"sortField": "bytesAllowed",
"sortDescending": True,
"searchFlags": ["users"] # IMPORTANTE: senza questo ritorna 0 risultati
}
response = requests.post(users_url, json=payload, headers=headers, verify=True)
if response.status_code != 200:
print(f" Errore recupero utenti {domain_name}: HTTP {response.status_code}")
return None
data = response.json()
if data.get("success") is False:
print(f" Errore API utenti {domain_name}: {data.get('message')}")
return None
users = data.get("results", [])
# Filtra: includi solo User (1), DomainAdmin (2), PrimaryDomainAdmin (3)
# Escludi Alias (4) e MailingList (5)
filtered = [u for u in users if u.get("accountType", 0) in (1, 2, 3)]
return filtered
def format_bytes(size_bytes: int) -> str:
"""Formatta i bytes in formato leggibile."""
if size_bytes < 1024:
return f"{size_bytes} B"
elif size_bytes < 1024 * 1024:
return f"{size_bytes / 1024:.2f} KB"
elif size_bytes < 1024 * 1024 * 1024:
return f"{size_bytes / (1024 * 1024):.2f} MB"
else:
return f"{size_bytes / (1024 * 1024 * 1024):.2f} GB"
def get_status_name(status: int) -> str:
"""Converte il codice status numerico in stringa leggibile."""
status_map = {
0: "Disabilitato",
1: "Attivo",
2: "Bloccato"
}
return status_map.get(status, f"Sconosciuto({status})")
def get_account_type_name(account_type: int) -> str:
"""Converte il codice accountType in stringa leggibile."""
type_map = {
1: "User",
2: "DomainAdmin",
3: "PrimaryDomainAdmin",
4: "Alias",
5: "MailingList"
}
return type_map.get(account_type, f"Tipo({account_type})")
def print_domains_table(domains: list[dict]) -> None:
"""Stampa la tabella dei domini."""
domains_sorted = sorted(domains, key=lambda d: d.get("name", "").lower())
total_users = sum(d.get("userCount", 0) for d in domains_sorted)
total_disk = sum(d.get("size", 0) for d in domains_sorted)
enabled = sum(1 for d in domains_sorted if d.get("isEnabled", False))
print("\n" + "=" * 90)
print("ELENCO DOMINI SMARTERMAIL")
print("=" * 90)
print(f"\n Domini totali: {len(domains_sorted)} | Utenti totali: {total_users:,}")
print(f" Spazio disco totale: {format_bytes(total_disk)} | Attivi: {enabled} | Disabilitati: {len(domains_sorted) - enabled}")
print("\n" + "-" * 90)
print(f" {'Dominio':<30} {'Stato':<10} {'Utenti':<12} {'Disco':<15} {'Quota':<15}")
print("-" * 90)
for d in domains_sorted:
name = d.get("name", "N/A")
status = "Attivo" if d.get("isEnabled", False) else "Disab."
user_count = d.get("userCount", 0)
user_limit = d.get("userLimit", 0)
users_str = f"{user_count}" + (f"/{user_limit}" if user_limit > 0 else "")
size = d.get("size", 0)
max_size = d.get("maxSize", 0)
quota_str = format_bytes(max_size) if max_size > 0 else "Illimitato"
print(f" {name:<30} {status:<10} {users_str:<12} {format_bytes(size):<15} {quota_str:<15}")
print("-" * 90)
def print_users_table(domain_name: str, users: list[dict]) -> None:
"""Stampa la tabella utenti di un dominio."""
print(f"\n {'Email':<35} {'Nome':<20} {'Stato':<12} {'Tipo':<18} {'Disco usato':<15} {'Quota':<15} {'Ultimo login':<12}")
print(" " + "-" * 127)
for u in sorted(users, key=lambda x: x.get("userName", "")):
email = f"{u.get('userName', '?')}@{domain_name}"
display_name = u.get("displayName", "") or ""
if len(display_name) > 18:
display_name = display_name[:17] + "."
status = get_status_name(u.get("status", 0))
acc_type = get_account_type_name(u.get("accountType", 1))
bytes_used = u.get("bytesUsed", 0)
bytes_allowed = u.get("bytesAllowed")
quota_str = format_bytes(bytes_allowed) if bytes_allowed else "Illimitato"
last_login = u.get("lastLoginTime") or u.get("lastLogin") or ""
if last_login:
try:
dt = datetime.fromisoformat(last_login.replace("Z", "+00:00"))
last_login = dt.strftime("%d/%m/%Y")
except (ValueError, AttributeError):
last_login = str(last_login)[:10]
# Protocolli abilitati
protocols = []
if u.get("isWebmailEnabled"): protocols.append("Web")
if u.get("isImapEnabled"): protocols.append("IMAP")
if u.get("isPopEnabled"): protocols.append("POP")
if u.get("isEasEnabled"): protocols.append("EAS")
if u.get("isMapiEwsEnabled"): protocols.append("MAPI/EWS")
print(f" {email:<35} {display_name:<20} {status:<12} {acc_type:<18} {format_bytes(bytes_used):<15} {quota_str:<15} {last_login:<12}")
total_bytes = sum(u.get("bytesUsed", 0) for u in users)
active = sum(1 for u in users if u.get("status", 0) == 1)
print(f"\n Totale: {len(users)} utenti ({active} attivi) - Disco usato: {format_bytes(total_bytes)}")
def main():
# 1. Autenticazione
print("Connessione a SmarterMail...")
token = authenticate(SMARTERMAIL_URL, USERNAME, PASSWORD)
if not token:
return
# 2. Recupero elenco domini
print("Recupero elenco domini...")
domains = get_domains_list(SMARTERMAIL_URL, token)
if domains is None:
return
print_domains_table(domains)
# 3. Recupero utenti per ogni dominio (come fa ElencoUtenti.cshtml.cs)
# Filtra solo domini con utenti, ordinati per nome
domains_with_users = sorted(
[d for d in domains if d.get("userCount", 0) > 0],
key=lambda d: d.get("name", "").lower()
)
print(f"\n{'=' * 90}")
print(f"ELENCO UTENTI PER DOMINIO ({len(domains_with_users)} domini con utenti)")
print(f"{'=' * 90}")
grand_total_users = 0
grand_total_bytes = 0
for i, domain in enumerate(domains_with_users, 1):
domain_name = domain.get("name", "")
expected_count = domain.get("userCount", 0)
print(f"\n[{i}/{len(domains_with_users)}] Caricamento {domain_name} (~{expected_count} utenti)...")
users = get_domain_users(SMARTERMAIL_URL, token, domain_name)
if users is not None:
print_users_table(domain_name, users)
grand_total_users += len(users)
grand_total_bytes += sum(u.get("bytesUsed", 0) for u in users)
else:
print(f" Impossibile caricare gli utenti di {domain_name}")
print(f"\n{'=' * 90}")
print(f"RIEPILOGO: {grand_total_users} utenti totali - Disco usato: {format_bytes(grand_total_bytes)}")
print(f"{'=' * 90}\n")
if __name__ == "__main__":
main()