from django.conf import settings
from django.core.exceptions import SuspiciousOperation
import types
import re

USE_X_FORWARDED_FOR = getattr(settings, 'USE_X_FORWARDED_FOR', False)

ip_validation_re = re.compile(r"^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$")

class RequestAddrMiddleware(object):
	"""
	Adds a get_addr() method to the request object which returns an IP address based on X-Forwarded-For header if
	present and USE_X_FORWARDED_FOR setting is True (default is False) or REMOTE_ADDR meta attribute otherwise.
	"""

	def process_request(self, request):
		"""Called on each request, before Django decides which view to execute."""

		def get_addr(inst):
			"""Returns the client IP address using the environment or request headers."""

			if USE_X_FORWARDED_FOR and 'HTTP_X_FORWARDED_FOR' in inst.META:
				addr = inst.META['HTTP_X_FORWARDED_FOR'].split(",",1)[0].strip()
			elif 'REMOTE_ADDR' in inst.META:
				addr = inst.META['REMOTE_ADDR']
			else:
				addr = "127.0.0.1" #Couldn't determine actual client IP!

			if not ip_validation_re.match(addr):
				raise SuspiciousOperation("Invalid client IP: %s" % addr)

			return addr

		request.get_addr = types.MethodType(get_addr, request, request.__class__)