Версия для печати
Суббота, 14 Январь 2023 22:37

Скрипт для бэкапа почтовых ящиков Zimbra

Автор
Оцените материал
(9 голосов)

Устроившись на новую работу, я получил в своё владение работающий настроенный сервер Zimbra версии 8.8.15_GA_3847. Работает этот сервер на виртуальной машине, и бэкапирование делается двумя методами:

1. бэкапится сама виртуальная машина
2.1. бэкапятся почтовые ящики всех пользователей раз в неделю
2.2. бэкапятся почтовые ящики некоторых избранных пользователей по будням. 

Про бэкап виртуальной машины всё понятно. А вот бэкапы почтовых ящиков делаются скриптами. Предыдущий системный администратор, скорее всего, скрипты взял от сюда:

https://habr.com/ru/post/439728/

Но я их немного причесал под себя, сделав маленькие доработки.

Итак, по будням, каждый вечер запускается вот такой скрипт:

#!/bin/bash
# В этом скрипте Зимбра выгружает ящики на локальный диск, не сжимая. В обычный tar.
# Потом ящики сжимаются через pbzip2 и копируются в /mnt/mailbox_bkp/Arch-day.
# Выгружаются только ящики, заданные в файле /root/Backup_scripts/MBoxesList
PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin"
#Куда положить бэкап
Path="/mnt/mailbox_bkp/Backups"
#Куда положить архив бэкапа
ArchPath="/mnt/mailbox_bkp/Arch-day"
#Список ящиков
MBoxes="/root/Backup_scripts/MBoxesList"
#Текущая дата
CDate=$(date +%d-%m-%Y)
#Куда писать логи
log="/mnt/mailbox_bkp/BackUpLog.txt"
#Начало работы скрипта
echo -en
echo -en "day BackUp selected MailBoxes started `date '+%d.%m.%y'` in $(date +%T)\n" >> $log
#Очистка локального каталога от предыдущих выгрузок
echo -en "Clear catalog $Path\n" >> $log
rm -Rf $Path/*
#Проверка несуществования каталога для резервного копирования
if [ ! -d $Path ]; then
	#Создание каталога хранения резервных копий
	echo "Создание каталога хранения резервных копий..."
	mkdir -p $Path
	if [ $? -eq 0 ]; then
		echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
		echo
		echo -en "BackUp dirctory was created in $(date +%T)\n" >> $log
	else
		echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
		echo
		echo -en "BackUp dirctory was NOT created in $(date +%T)\n" >> $log
	fi
else
echo "Каталог хранения резервных копий существует, проверка существования каталога на сегодняшнее число..."
fi
#Првоерка несуществования каталога на сегодняшнюю дату
if [ ! -d $Path/$CDate ]; then
	echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
	echo
	#Создание каталога хранения резервных копий на сегодняшнее число
	echo "Создание каталога хранения резервных копий на сегодняшнее число..."
	mkdir -p $Path/$CDate
	if [ $? -eq 0 ]; then
		echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
		echo
		echo -en "BackUp CDate dirctory was created in $(date +%T)\n" >> $log
	else
		echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
		echo
		echo -en "BackUp CDate dirctory was NOT created in $(date +%T)\n" >> $log
	fi
else
	echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
	echo
	echo "Каталог хранения резервных копий существует, запись списка ящиков в файл..."
fi
#создание резервных копий каждого ящика из списка
for MailBox in $( cat $MBoxes); do
	echo "Создание резервной копии ящика $MailBox..."
	/opt/zimbra/bin/zmmailbox -z -m $MailBox getRestUrl "//?fmt=tar" > $Path/$CDate/$MailBox
	if [ $? -eq 0 ]; then
		#Вывод результата создания резервной копии для каждого ящика
		echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
		echo
		echo -en "Mail Box $MailBox BackUp created in $(date +%T)\n" >> $log
	else
		echo -n "$(tput hpa $(tput cols))$(tput cub 6)[FAIL]"
		echo
		echo -en "Mail Box $MailBox BackUp is NOT created! in $(date +%T)\n" >> $log
	fi
done
#Создание архива и работа с архивами
#Проверка несуществования каталога для архивирвоания
if [ ! -d $ArchPath ]; then
	#Создание каталога хранения архивов
	echo "Создание каталога хранения архивов..."
	mkdir -p $ArchPath
	if [ $? -eq 0 ]; then
		echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
		echo
		echo -en "Archive dirctory was created in $(date +%T)\n" >> $log
	else
		echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
		echo
		echo -en "Archive dirctory was NOT created in $(date +%T)\n" >> $log
	fi
else
echo "Каталог хранения архивов существует, архививрование..."
fi
# создать tar используя сжатие pbzip2
tar -C $Path/ -cf $ArchPath/$CDate.tar --use-compress-prog=pbzip2 $CDate
if [ $? -eq 0 ]; then
	echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
	echo
	echo -en "Archive created in $(date +%T)\n" >> $log
	#Удаление старых архивов бэкапов (старше 2 недель)
	echo "Удаление старых архивов резервных копий (старше 2 недель)..."
	find $ArchPath -type f -mtime +14 -delete
	if [ $? -eq 0 ]; then
		echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
		echo
		echo -en "Old BackUps archives was deleted\n" >> $log
	else
		echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
		echo
		echo -en "Old BackUps archives is NOT deleted in $(date +%T)\n" >> $log
	fi
else
	echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
	echo
	echo -en "Archive is NOT created in $(date +%T)\n" >> $log
fi
echo "BackUp job finished in $(date +%T)"
#запись в лог-файл времени окончания резервного копирования
echo -en "BackUp job finished `date '+%d.%m.%y'` in $(date +%T)\n" >> $log
echo -en "_____________________________________________\n" >> $log

Скачать можно тут:

https://disk.yandex.ru/d/pNwWhgSYZr3A-w

В нём нужно лишь под себя поправить переменные:

#Куда положить бэкап
Path=

#Куда положить архив бэкапа
ArchPath=

#Список ящиков
MBoxes=

#Куда писать логи
log=

В файл со списком ящиков записаны нужные адреса в каждой отдельной строчке:

ivanov@mydomain.ru
petrov@mydomain.ru
utkin@mydomain.ru
popov@mydomain.ru
sidorov@mydomain.ru
kuznecov@mydomain.ru
и т.д.

По субботам запускается скрипт, бэкапящий все ящики заданного домена:

#!/bin/bash
# В этом скрипте Зимбра выгружает ящики на локальный диск, не сжимая. В обычный tar.
# Потом ящики сжимаются через pbzip2 и копируются в /mnt/mailbox_bkp/Arch-week.
PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin"
#Куда положить бэкап
Path="/mnt/mailbox_bkp/Backups"
#Куда положить архив бэкапа
ArchPath="/mnt/mailbox_bkp/Arch-week"
#Название домена Zimbra
ZDomain="mydomain.ru"
#Список ящиков
MBoxes="/mnt/mailbox_bkp/MBoxesList"
#Текущая дата
CDate=$(date +%d-%m-%Y)
#Куда писать логи
log="/mnt/mailbox_bkp/BackUpLog.txt"
#Начало работы скрипта
echo -en
echo -en "BackUp ALL MailBoxes started `date '+%d.%m.%y'` in $(date +%T)\n" >> $log
#Очистка локального каталога от предыдущих выгрузок
echo -en "Clear catalog $Path\n" >> $log
rm -Rf $Path/*
#Проверка несуществования каталога для резервного копирования
if [ ! -d $Path ]; then
	#Создание каталога хранения резервных копий
	echo "Создание каталога хранения резервных копий..."
	mkdir -p $Path
	if [ $? -eq 0 ]; then
		echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
		echo
		echo -en "BackUp dirctory was created in $(date +%T)\n" >> $log
	else
		echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
		echo
		echo -en "BackUp dirctory was NOT created in $(date +%T)\n" >> $log
	fi
else
echo "Каталог хранения резервных копий существует, проверка существования каталога на сегодняшнее число..."
fi
#Првоерка несуществования каталога на сегодняшнюю дату
if [ ! -d $Path/$CDate ]; then
	echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
	echo
	#Создание каталога хранения резервных копий на сегодняшнее число
	echo "Создание каталога хранения резервных копий на сегодняшнее число..."
	mkdir -p $Path/$CDate
	if [ $? -eq 0 ]; then
		echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
		echo
		echo -en "BackUp CDate dirctory was created in $(date +%T)\n" >> $log
	else
		echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
		echo
		echo -en "BackUp CDate dirctory was NOT created in $(date +%T)\n" >> $log
	fi
else
	echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
	echo
	echo "Каталог хранения резервных копий существует, запись списка ящиков в файл..."
fi
#Запись списка ящиков в файл
/opt/zimbra/bin/zmprov -l gaa $ZDomain > $MBoxes
#Вывод результата записи списка
if [ $? -eq 0 ]; then
	echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
	echo
	echo -en "Mail Boxes list created in $(date +%T)\n" >> $log
else
	echo -n "$(tput hpa $(tput cols))$(tput cub 6)[FAIL]"
	echo
	echo -en "Mail Box list is NOT created! in $(date +%T)\n" >> $log
	exit
fi
#создание резервных копий каждого ящика из списка
for MailBox in $( cat $MBoxes); do
	echo "Создание резервной копии ящика $MailBox..."
	/opt/zimbra/bin/zmmailbox -z -m $MailBox getRestUrl "//?fmt=tar" > $Path/$CDate/$MailBox
	if [ $? -eq 0 ]; then
		#Вывод результата создания резервной копии для каждого ящика
		echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
		echo
		echo -en "Mail Box $MailBox BackUp created in $(date +%T)\n" >> $log
	else
		echo -n "$(tput hpa $(tput cols))$(tput cub 6)[FAIL]"
		echo
		echo -en "Mail Box $MailBox BackUp is NOT created! in $(date +%T)\n" >> $log
	fi
done
#Очищаем файл со списком ящиков
echo "Очистка файла со споском ящиков..."
echo -n > $MBoxes
if [ $? -eq 0 ]; then
	echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
	echo
	echo -en "File $MBoxes clear\n" >> $log
else
	echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
	echo
	echo -en "File $MBoxes can NOT be cleared\n" >> $log
fi
#Создание архива и работа с архивами
#Проверка несуществования каталога для архивирвоания
if [ ! -d $ArchPath ]; then
	#Создание каталога хранения архивов
	echo "Создание каталога хранения архивов..."
	mkdir -p $ArchPath
	if [ $? -eq 0 ]; then
		echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
		echo
		echo -en "Archive dirctory was created in $(date +%T)\n" >> $log
	else
		echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
		echo
		echo -en "Archive dirctory was NOT created in $(date +%T)\n" >> $log
	fi
else
echo "Каталог хранения архивов существует, архививрование..."
fi
# создать tar используя сжатие pbzip2
tar -C $Path/ -cf $ArchPath/$CDate.tar --use-compress-prog=pbzip2 $CDate
if [ $? -eq 0 ]; then
	echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
	echo
	echo -en "Archive created in $(date +%T)\n" >> $log
	#Удаление старых архивов бэкапов (старше месяца)
	echo "Удаление старых архивов резервных копий (старше месяца)..."
	find $ArchPath -type f -mtime +32 -delete
	if [ $? -eq 0 ]; then
		echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
		echo
		echo -en "Old BackUps archives was deleted\n" >> $log
	else
		echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
		echo
		echo -en "Old BackUps archives is NOT deleted in $(date +%T)\n" >> $log
	fi
else
	echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
	echo
	echo -en "Archive is NOT created in $(date +%T)\n" >> $log
fi
echo "BackUp job finished in $(date +%T)"
#запись в лог-файл времени окончания резервного копирования
echo -en "BackUp job finished `date '+%d.%m.%y'` in $(date +%T)\n" >> $log
echo -en "_____________________________________________\n" >> $log

Скачать можно здесь же:

https://disk.yandex.ru/d/pNwWhgSYZr3A-w

И в нём также надо изменить следующие переменные под себя: 

#Куда положить бэкап
Path=

#Куда положить архив бэкапа
ArchPath=

#Название домена Zimbra
ZDomain=

#Список ящиков
MBoxes=

#Куда писать логи
log=

Для работы этих двух скриптов требуется пакет pbzip2

Собственно, у меня эти оба скрипта используют один и тот же файл для лога и один и тот же каталог для выгрузки ящиков. Но сжатые архивы с выгрузкой ящиков кладут в разные каталоги:

Здесь лежат выгрузки почтовых ящиков:

Также есть скрипт восстановления почты из ранее сделанной выгрузки. Вот он:

#!/bin/bash
#Где лежат бэкапы
Path="/mnt/mailbox_bkp/Backups"
#Название домена Zimbra
ZDomain="mydomain.ru"
#Список ящиков
MBoxes="/mnt/mailbox_bkp/MBoxesList"
#Куда писать логи
log="/mnt/mailbox_bkp/RestoreLog.txt"
read -p "Дата резервной копии, которую необходимо восстановить в формате 02-09-2001: " Date
if ! [ -d $Path/$Date ]; then
echo "Нет резервных копий на указанную дату."
echo -en "No BackUp file at $Date $(date +%T)\n" > $log
exit
fi
read -p "Введите имя почтового ящика (без указания домена), или ALL для восстановления всех почтовых ящиков на указанную дату: " A
if [[ "$A" = "ALL" || "$A" = "all" ]]; then
	echo -en "Restore started in $(date +%T)\n" >> $log
	#Запись списка ящиков в файл
	ls "$Path/$Date" > $MBoxes
	for MailBox in $( cat $MBoxes); do
		#Проверка существования ящика
		echo "Проверка существования ящика $MailBox"
		Result=$(/opt/zimbra/bin/zmprov getMailboxInfo $MailBox)
		if [ $? -eq 0 ]; then
			echo -n "$(tput hpa $(tput cols))$(tput cub 6) [OK]"
			echo
			echo -en "Start restore job for $MailBox $(date +%T)\n" >> $log
			echo "Ящик $MailBox существует. Восстановление..."
		else
			echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
			echo
			echo -en "Mail Box $MailBox does not exist, creating Mail Box $MailBox $(date +%T)\n" >> $log
			echo "Ящик $MailBox не существует. Создание ящика $MailBox..."
			#Проверка создания ящика
			Result=$(/opt/zimbra/bin/zmprov ca $MailBox 12345678 displayName "$MailBox")
			if [ $? -eq 0 ]; then
				echo -n "$(tput hpa $(tput cols))$(tput cub 6) [OK]"
				echo
				echo -en "Mail Box $MailBox is created, starting restore $(date +%T)\n" >> $log
				echo "Ящик $MailBox создан успешно. Восстановление..."
			else
				echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
				echo
				echo -en "Can NOT create Mail Box $MailBox ! $(date +%T)\n" >> $log
				echo "Ящик $MailBox создать не удалось."
			fi
		fi
		#Восстановление ящика
		Result=$(/opt/zimbra/bin/zmmailbox -z -m $MailBox postRestURL "//?fmt=tar&resolve=replace" $Path/$Date/$MailBox)
		if [ $? -eq 0 ]; then
			echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
			echo
			echo -en "Ящик $MailBox восстановлен в $(date +%T)\n" >> $log
		else
			echo -n "$(tput hpa $(tput cols))$(tput cub 6)[FAIL]"
			echo
			echo -en "Ящик $MailBox НЕ восстановлен! $(date +%T)\n" >> $log
		fi	
	done
else
	#Проверка существования запрошенной резервной копии
	MailBox="$A@$ZDomain"
	if [ -a $Path/$Date/$MailBox ]; then
		#Проверка существования ящика
		echo "Проверка существования ящика $MailBox..."
		Result=$(/opt/zimbra/bin/zmprov getMailboxInfo $MailBox)
		if [ $? -eq 0 ]; then
			echo -n "$(tput hpa $(tput cols))$(tput cub 6) [OK]"
			echo
			echo -en "Start restore job for $MailBox $(date +%T)\n" >> $log
			echo "Ящик $MailBox существует. Восстановление..."
		else
			echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
			echo
			echo -en "Mail Box $MailBox does not exist $(date +%T)\n" >> $log
			echo "Ящик $MailBox не существует."
			read -p "Хотите создать почтовый ящик с указанным именем и восстановить в него резервную копию? [N]: " B
			if [[ "$B" = "Y" || "$B" = "y" ]]; then
				echo "Создание почтового ящика $MailBox..."
				Result=$(/opt/zimbra/bin/zmprov ca $MailBox 12345678 displayName "$MailBox")
				#Проверка создания ящика
				if [ $? -eq 0 ]; then
					echo -n "$(tput hpa $(tput cols))$(tput cub 6) [OK]"
					echo
					echo -en "Mail Box $MailBox is created, starting restore $(date +%T)\n" >> $log
					echo "Ящик $MailBox создан успешно. Восстановление..."
				else
					echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
					echo
					echo -en "Can NOT create Mail Box $MailBox ! $(date +%T)\n" >> $log
					echo "Ящик $MailBox создать не удалось. Завершение работы скрипта..."
					exit
				fi
			else
				#Ящик не будет создан, нечего восстанавливать. Выход
				echo "Ящик не будет создан. Завершение работы скрипта"
				exit
			fi
		fi
		#Восстановление ящика
		Result=$(/opt/zimbra/bin/zmmailbox -z -m $MailBox postRestURL "//?fmt=tar&resolve=replace" $Path/$Date/$MailBox)
		if [ $? -eq 0 ]; then
			echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
			echo
			echo -en "Ящик $MailBox восстановлен в $(date +%T)\n" >> $log
		else
			echo -n "$(tput hpa $(tput cols))$(tput cub 6)[FAIL]"
			echo
			echo -en "Ящик $MailBox НЕ восстановлен! $(date +%T)\n" >> $log
		fi	
	else
		#Запрошенной резервной копии не существует
		echo "Запрошенной резервной копии не существует. Завершение работы скрипта"
		echo -en "Required BackUp file is not exist\n" >> $log
		exit
	fi
fi	
#Очищаем файл со списком ящиков
echo "Очистка файла со споском ящиков..."
echo -n > $MBoxes
if [ $? -eq 0 ]; then
	echo -n "$(tput hpa $(tput cols))$(tput cub 6)[OK]"
	echo
else
	echo -n "$(tput hpa $(tput cols))$(tput cub 6) [FAIL]"
	echo
fi
echo "BackUp job finished in $(date +%T) $(date +%T)"
#запись в лог-файл времени окончания резервного копирования
echo -en "Restore job complete in $(date +%T)\n" >> $log
echo -en "____________________________________\n" >> $log

Скачать снова здесь: 

https://disk.yandex.ru/d/pNwWhgSYZr3A-w

Вкратце опишу, как им пользоваться. Для начала, надо в нём поправить переменные под себя:

#Где лежат бэкапы
Path=

#Название домена Zimbra
ZDomain=

#Список ящиков
MBoxes=

#Куда писать логи
log=

Допустим, хочу восстановить какой-нибудь старый ящик, который давно удалили, и о котором вспомнили уже спустя время. Сначала надо достать выгрузки почтовых ящиков из общего архива и положить их в каталог из переменной Path. У меня это каталог /mnt/mailbox_bkp/Backups. Общий архив лежит в каталоге /mnt/mailbox_bkp/Arch-day. Делаю это командой: 

tar -xvf /mnt/mailbox_bkp/Arch-day/08-09-2022.tar --directory /mnt/mailbox_bkp/Backups 

Получу вот такой результат:

Если не получится распаковать сразу через tar, то можно попробовать разжать файл с помощью самой pbzip2, команда будет примерно такая: 

pbzip2 -dk -p4 < /mnt/mailbox_bkp/Arch-day/08-09-2022.tar > /mnt/mailbox_bkp/backup.tar

Что разжать: /mnt/mailbox_bkp/Arch-day/08-09-2022.tar
Получаемый расжатый файл: /mnt/mailbox_bkp/backup.tar
-p4 – задействовать 4 потока процессора. 

После этого уже распаковать разжатый файл через tar: 

tar -xvf /mnt/mailbox_bkp/backup.tar --directory /mnt/mailbox_bkp/Backups 

Далее уже можно запускать скрипт восстановления почтового ящика. Скрипт сначала попросит ввести дату резервной копии, которую надо восстановить. У меня это будет 08-09-2022. Если каталога с указанной датой не обнаружится, то скрипт завершит работу. А если каталог с такой датой присутствует, то скрипт спросит, какой ящик восстановить, либо попросит написать ALL для восстановления всех ящиков, которые найдёт в каталоге 08-09-2022.

Собственно, эти бэкап-скрипты можно переделать, чтобы общий архив делался обычным gzip или bzip2 и не заморачиваться с pbzip2, но тогда будет ощутимый проигрыш по времени, ведь, в отличие от gzip и bzip2, pbzip2 использует многопоточность.

Система работает на CentOS 7, но и на других Линуксах эти скрипты должны нормально работать.

Донаты принимаются на кошельки:

Yoomoney:
4100118091867315

BTC:
bc1qzw9vam8mv6derwscxl0vrnd6m9t2rpjg273mna

ETH / BNB BSC / Polygon MATIC:
0x5cc07FF76490350ac6112fbFdA1B545Bc794602F

Tron:
TJUz8sJr9XYMjVqzmFNnCzzRWfPa57X2RV

USDT/USDC в сетях ETH/BSC/Polygon:
0x5cc07FF76490350ac6112fbFdA1B545Bc794602F

USDT в сети TRX (Tron):
TJUz8sJr9XYMjVqzmFNnCzzRWfPa57X2RV

LTC:
LRMZaFCSyCT6FUF62WEX1BokWV7v2dh2zo

Doge:
DTEnGLZRps9XaWNtAhchJWSeD4uTNDRxg7

XMR:
4A6uP1WxEc7HktToZFyiJuK6YmjdL8bSn2aY653qPwABhT4Y56iFuedgHcmpvLwWE55u8qkjGc715ZJs761FqedA8gkgznr

TON:
UQAdSPiWIDx2Q1VIeezkUV3s4sNlZM90w2ohSO6bD2-okwgY

Прочитано 714 раз Последнее изменение Воскресенье, 15 Январь 2023 00:53
Николай