import re # Curated IANA timezone list for the dropdown. Validation accepts any entry from this set. VALID_TIMEZONES = [ 'UTC', # Americas 'America/New_York', 'America/Detroit', 'America/Indiana/Indianapolis', 'America/Chicago', 'America/Denver', 'America/Phoenix', 'America/Los_Angeles', 'America/Anchorage', 'America/Adak', 'Pacific/Honolulu', 'America/Toronto', 'America/Vancouver', 'America/Winnipeg', 'America/Halifax', 'America/St_Johns', 'America/Mexico_City', 'America/Bogota', 'America/Lima', 'America/Santiago', 'America/Caracas', 'America/Sao_Paulo', 'America/Argentina/Buenos_Aires', 'America/Montevideo', # Europe 'Europe/London', 'Europe/Dublin', 'Europe/Lisbon', 'Europe/Paris', 'Europe/Berlin', 'Europe/Amsterdam', 'Europe/Brussels', 'Europe/Madrid', 'Europe/Rome', 'Europe/Zurich', 'Europe/Vienna', 'Europe/Stockholm', 'Europe/Oslo', 'Europe/Copenhagen', 'Europe/Helsinki', 'Europe/Warsaw', 'Europe/Prague', 'Europe/Budapest', 'Europe/Bucharest', 'Europe/Athens', 'Europe/Istanbul', 'Europe/Moscow', 'Europe/Kyiv', # Africa 'Africa/Casablanca', 'Africa/Lagos', 'Africa/Cairo', 'Africa/Nairobi', 'Africa/Johannesburg', # Asia 'Asia/Dubai', 'Asia/Tbilisi', 'Asia/Tehran', 'Asia/Karachi', 'Asia/Kolkata', 'Asia/Colombo', 'Asia/Dhaka', 'Asia/Yangon', 'Asia/Bangkok', 'Asia/Ho_Chi_Minh', 'Asia/Singapore', 'Asia/Kuala_Lumpur', 'Asia/Jakarta', 'Asia/Shanghai', 'Asia/Hong_Kong', 'Asia/Taipei', 'Asia/Manila', 'Asia/Seoul', 'Asia/Tokyo', 'Asia/Yakutsk', 'Asia/Vladivostok', # Australia / Pacific 'Australia/Perth', 'Australia/Darwin', 'Australia/Adelaide', 'Australia/Brisbane', 'Australia/Sydney', 'Australia/Melbourne', 'Australia/Hobart', 'Pacific/Auckland', 'Pacific/Fiji', 'Pacific/Guam', 'Pacific/Honolulu', ] _TIMEZONE_SET = set(VALID_TIMEZONES) def _strip(value, pattern, max_len): return re.sub(pattern, '', str(value).strip())[:max_len] def text(value, max_len=200): """General description: letters, digits, spaces, basic punctuation. No quotes/braces/brackets/slashes.""" return _strip(value, r'''["'{}\[\]\\/<>;`^~]''', max_len) def name(value, max_len=64): """Label/name: letters, digits, spaces, hyphens, underscores, dots.""" return _strip(value, r'[^A-Za-z0-9 \-_.]', max_len) def hostname(value, max_len=253): """Hostname or domain: letters, digits, hyphens, dots. Lowercased.""" return _strip(value.lower(), r'[^a-z0-9\-.]', max_len) def ip(value, max_len=45): """IPv4 or IPv6 address: digits, dots, colons, hex letters.""" return _strip(value, r'[^0-9a-fA-F.:]', max_len) def ip_or_cidr(value, max_len=49): """IP address or CIDR subnet: adds forward slash.""" return _strip(value, r'[^0-9a-fA-F.:/]', max_len) def mac(value, max_len=17): """MAC address: hex digits and colons.""" return _strip(value.upper(), r'[^0-9A-F:]', max_len) def url(value, max_len=500): """URL: printable ASCII except quotes, braces, brackets, backslash, spaces.""" return _strip(value, r'''["'{}\[\]\\ ]''', max_len) def interface_name(value, max_len=32): """Network interface name: letters, digits, hyphens, underscores, dots.""" return _strip(value, r'[^A-Za-z0-9\-_.]', max_len) def port(value): """Port number string, validated 1-65535. Returns '' if invalid.""" digits = re.sub(r'[^0-9]', '', str(value)) try: n = int(digits) if 1 <= n <= 65535: return str(n) except (ValueError, TypeError): pass return '' def time_24h(value, max_len=5): """24-hour time HH:MM: digits and colon only.""" return _strip(value, r'[^0-9:]', max_len) def email(value, max_len=254): """Email address: letters, digits, @, dot, hyphen, underscore, plus. Lowercased.""" return _strip(value.lower(), r'[^a-z0-9@.\-_+]', max_len) def timezone(value): """Timezone string: must be in VALID_TIMEZONES list. Returns '' if not found.""" return value if value in _TIMEZONE_SET else ''