import logging
import itertools
import ftplib
import time

from parallels.hosting_check import DomainIssue
from parallels.hosting_check import Severity, FTPAuthIssueType
from parallels.hosting_check.messages import MSG

logger = logging.getLogger(__name__)

class FTPAuthChecker(object):
	def check(self, domains_to_check):
		issues = []
		for domain_to_check in domains_to_check:
			self._check_single_domain(domain_to_check, issues)
		return issues

	def _check_single_domain(self, domain, issues):
		if not self._check_web_ip_present(domain, issues):
			return 

		for user in itertools.chain(domain.users, domain.inactive_users):
			if not self._can_verify_user(user):
				issues.append(
					DomainIssue(
						domain_name=domain.domain_name, 
						severity=Severity.WARNING, 
						category=FTPAuthIssueType.ENCRYPTED_PASSWORD,
						problem=MSG(
							FTPAuthIssueType.ENCRYPTED_PASSWORD,
							server_ip=domain.web_server_ip, 
							login=user.login)))
			elif self._user_is_active(domain, user):
				self._check_active_user(domain, user, issues)
			else:
				self._check_inactive_user(domain, user, issues)

	def _check_web_ip_present(self, domain_to_check, issues):
		if domain_to_check.web_server_ip is None:
			issues.append(
				DomainIssue(
					domain_name=domain_to_check.domain_name,
					severity=Severity.WARNING, 
					category=FTPAuthIssueType.WEB_IP_NOT_DEFINED,
					problem=MSG(FTPAuthIssueType.WEB_IP_NOT_DEFINED)
				)
			)
			return False
		return True

	def _can_verify_user(self, user):
		return user.password_type == 'plain'

	def _user_is_active(self, domain, user):
		return not domain.inactive_users or not user in domain.inactive_users

	def _check_active_user(self, domain_to_check, user, issues):
		try:
			auth_successful, message = self._check_ftp_auth(
				domain_to_check.web_server_ip, user
			)
			if not auth_successful:
				issues.append(
					DomainIssue(
						domain_name=domain_to_check.domain_name,
						severity=Severity.ERROR, 
						category=FTPAuthIssueType.CHECK_FAILED,
						problem=MSG(
							FTPAuthIssueType.CHECK_FAILED,
							server_ip=domain_to_check.web_server_ip, 
							login=user.login, 
							reason=message
						)
					)
				)
		except KeyboardInterrupt:
			# for compatibility with python 2.4
			raise
		except Exception, e:
			logger.debug(u"Exception:", exc_info=e)
			issues.append(
				DomainIssue(
					domain_name=domain_to_check.domain_name,
					severity=Severity.WARNING, 
					category=FTPAuthIssueType.INTERNAL_ERROR,
					problem=MSG(
						FTPAuthIssueType.INTERNAL_ERROR,
						server_ip=domain_to_check.web_server_ip,
						login=user.login,
						reason=str(e)
					)
				)
			)

	def _check_inactive_user(self, domain_to_check, user, issues):
		try:
			auth_successful, _ = self._check_ftp_auth(
				domain_to_check.web_server_ip, user
			)
			if auth_successful:
				issues.append(
					DomainIssue(
						domain_name=domain_to_check.domain_name,
						severity=Severity.ERROR, 
						category=FTPAuthIssueType.INACTIVE_USER_CAN_CONNECT,
						problem=MSG(
							FTPAuthIssueType.INACTIVE_USER_CAN_CONNECT,
							server_ip=domain_to_check.web_server_ip,
							login=user.login
						)
					)
				)
		except KeyboardInterrupt:
			# for compatibility with python 2.4
			raise
		except Exception, e:
			logger.debug(u"Exception:", exc_info=e)
			issues.append(
				DomainIssue(
					domain_name=domain_to_check.domain_name,
					severity=Severity.WARNING, 
					category=FTPAuthIssueType.INTERNAL_ERROR,
					problem=MSG(
						FTPAuthIssueType.INTERNAL_ERROR,
						server_ip=domain_to_check.web_server_ip,
						login=user.login,
						reason=str(e)
					)
				)
			)

	def _check_ftp_auth(self, web_server_ip, user):
		try:
			logger.debug(MSG(
				'ftp_log_checking', 
				user=user.login, server_ip=web_server_ip
			))
			self._check_ftp_login(web_server_ip, user.login, user.password)
			return True, None
		except ftplib.all_errors, e:
			logger.debug(u"Exception:", exc_info=e)
			return False, str(e)

	def _check_ftp_login(self, host, login, password):
		ftp = ftplib.FTP(host)
		try:
			ftp.login(login, password)
			ftp.dir(lambda x: None) # just execute a command
			# we sleep 250ms due to ftp server of PPA/Plesk works via xinetd.d
			# that has default limit for 50 connections in 10 seconds interval
			time.sleep(0.25)
		finally:
			ftp.quit()
