1# -*- coding: utf-8 -*-
2#
3# Basic plugin template created by:
4# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
5#               2007-2009 Andrew Resch <andrewresch@gmail.com>
6#               2009 Damien Churchill <damoxc@gmail.com>
7#               2010 Pedro Algarvio <pedro@algarvio.me>
8#               2017 Calum Lind <calumlind+deluge@gmail.com>
9#
10# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
11# the additional special exception to link portions of this program with the OpenSSL library.
12# See LICENSE for more details.
13#
14
15from __future__ import unicode_literals
16
17import os.path
18from functools import wraps
19from sys import exc_info
20
21import six
22from pkg_resources import resource_filename
23
24
25def get_resource(filename):
26    return resource_filename(__package__, os.path.join('data', filename))
27
28
29def raises_errors_as(error):
30    """Factory class that returns a decorator which wraps the decorated
31    function to raise all exceptions as the specified error type.
32
33    """
34
35    def decorator(func):
36        """Returns a function which wraps the given func to raise all exceptions as error."""
37
38        @wraps(func)
39        def wrapper(self, *args, **kwargs):
40            """Wraps the function in a try..except block and calls it with the specified args.
41
42            Raises:
43                Any exceptions as error preserving the message and traceback.
44
45            """
46            try:
47                return func(self, *args, **kwargs)
48            except Exception:
49                (value, tb) = exc_info()[1:]
50                six.reraise(error, value, tb)
51
52        return wrapper
53
54    return decorator
55
56
57def remove_zeros(ip):
58    """Removes unneeded zeros from ip addresses.
59
60    Args:
61        ip (str): The ip address.
62
63    Returns:
64        str: The ip address without the unneeded zeros.
65
66    Example:
67        000.000.000.003 -> 0.0.0.3
68
69    """
70    return '.'.join([part.lstrip('0').zfill(1) for part in ip.split('.')])
71
72
73class BadIP(Exception):
74    _message = None
75
76    def __init__(self, message):
77        super(BadIP, self).__init__(message)
78
79    def __set_message(self, message):
80        self._message = message
81
82    def __get_message(self):
83        return self._message
84
85    message = property(__get_message, __set_message)
86    del __get_message, __set_message
87
88
89class IP(object):
90    __slots__ = ('q1', 'q2', 'q3', 'q4', '_long')
91
92    def __init__(self, q1, q2, q3, q4):
93        self.q1 = q1
94        self.q2 = q2
95        self.q3 = q3
96        self.q4 = q4
97        self._long = 0
98        for q in self.quadrants():
99            self._long = (self._long << 8) | int(q)
100
101    @property
102    def address(self):
103        return '.'.join([str(q) for q in [self.q1, self.q2, self.q3, self.q4]])
104
105    @property
106    def long(self):
107        return self._long
108
109    @classmethod
110    def parse(cls, ip):
111        try:
112            q1, q2, q3, q4 = [int(q) for q in ip.split('.')]
113        except ValueError:
114            raise BadIP(_('The IP address "%s" is badly formed' % ip))
115        if q1 < 0 or q2 < 0 or q3 < 0 or q4 < 0:
116            raise BadIP(_('The IP address "%s" is badly formed' % ip))
117        elif q1 > 255 or q2 > 255 or q3 > 255 or q4 > 255:
118            raise BadIP(_('The IP address "%s" is badly formed' % ip))
119        return cls(q1, q2, q3, q4)
120
121    def quadrants(self):
122        return (self.q1, self.q2, self.q3, self.q4)
123
124    #    def next_ip(self):
125    #        (q1, q2, q3, q4) = self.quadrants()
126    #        if q4 >= 255:
127    #            if q3 >= 255:
128    #                if q2 >= 255:
129    #                    if q1 >= 255:
130    #                        raise BadIP(_('There is not a next IP address'))
131    #                    q1 += 1
132    #                else:
133    #                    q2 += 1
134    #            else:
135    #                q3 += 1
136    #        else:
137    #            q4 += 1
138    #        return IP(q1, q2, q3, q4)
139    #
140    #    def previous_ip(self):
141    #        (q1, q2, q3, q4) = self.quadrants()
142    #        if q4 <= 1:
143    #            if q3 <= 1:
144    #                if q2 <= 1:
145    #                    if q1 <= 1:
146    #                        raise BadIP(_('There is not a previous IP address'))
147    #                    q1 -= 1
148    #                else:
149    #                    q2 -= 1
150    #            else:
151    #                q3 -= 1
152    #        else:
153    #            q4 -= 1
154    #        return IP(q1, q2, q3, q4)
155
156    def __lt__(self, other):
157        if isinstance(other, ''.__class__):
158            other = IP.parse(other)
159        return self.long < other.long
160
161    def __gt__(self, other):
162        if isinstance(other, ''.__class__):
163            other = IP.parse(other)
164        return self.long > other.long
165
166    def __eq__(self, other):
167        if isinstance(other, ''.__class__):
168            other = IP.parse(other)
169        return self.long == other.long
170
171    def __repr__(self):
172        return '<%s long=%s address="%s">' % (
173            self.__class__.__name__,
174            self.long,
175            self.address,
176        )
177