73
64
server_interface = domain + ".Mandos"
 
74
65
client_interface = domain + ".Mandos.Client"
 
 
68
def timedelta_to_milliseconds(td):
 
 
69
    """Convert a datetime.timedelta object to milliseconds"""
 
 
70
    return ((td.days * 24 * 60 * 60 * 1000)
 
 
72
            + (td.microseconds // 1000))
 
77
74
def milliseconds_to_string(ms):
 
78
75
    td = datetime.timedelta(0, 0, 0, ms)
 
79
76
    return ("{days}{hours:02}:{minutes:02}:{seconds:02}"
 
80
 
            .format(days = "{}T".format(td.days) if td.days else "",
 
 
77
            .format(days = "{0}T".format(td.days) if td.days else "",
 
81
78
                    hours = td.seconds // 3600,
 
82
79
                    minutes = (td.seconds % 3600) // 60,
 
83
80
                    seconds = td.seconds % 60,
 
87
 
def rfc3339_duration_to_delta(duration):
 
88
 
    """Parse an RFC 3339 "duration" and return a datetime.timedelta
 
90
 
    >>> rfc3339_duration_to_delta("P7D")
 
92
 
    >>> rfc3339_duration_to_delta("PT60S")
 
93
 
    datetime.timedelta(0, 60)
 
94
 
    >>> rfc3339_duration_to_delta("PT60M")
 
95
 
    datetime.timedelta(0, 3600)
 
96
 
    >>> rfc3339_duration_to_delta("PT24H")
 
98
 
    >>> rfc3339_duration_to_delta("P1W")
 
100
 
    >>> rfc3339_duration_to_delta("PT5M30S")
 
101
 
    datetime.timedelta(0, 330)
 
102
 
    >>> rfc3339_duration_to_delta("P1DT3M20S")
 
103
 
    datetime.timedelta(1, 200)
 
106
 
    # Parsing an RFC 3339 duration with regular expressions is not
 
107
 
    # possible - there would have to be multiple places for the same
 
108
 
    # values, like seconds.  The current code, while more esoteric, is
 
109
 
    # cleaner without depending on a parsing library.  If Python had a
 
110
 
    # built-in library for parsing we would use it, but we'd like to
 
111
 
    # avoid excessive use of external libraries.
 
113
 
    # New type for defining tokens, syntax, and semantics all-in-one
 
114
 
    Token = collections.namedtuple("Token",
 
115
 
                                   ("regexp", # To match token; if
 
116
 
                                              # "value" is not None,
 
117
 
                                              # must have a "group"
 
119
 
                                    "value",  # datetime.timedelta or
 
121
 
                                    "followers")) # Tokens valid after
 
123
 
    # RFC 3339 "duration" tokens, syntax, and semantics; taken from
 
124
 
    # the "duration" ABNF definition in RFC 3339, Appendix A.
 
125
 
    token_end = Token(re.compile(r"$"), None, frozenset())
 
126
 
    token_second = Token(re.compile(r"(\d+)S"),
 
127
 
                         datetime.timedelta(seconds=1),
 
128
 
                         frozenset((token_end,)))
 
129
 
    token_minute = Token(re.compile(r"(\d+)M"),
 
130
 
                         datetime.timedelta(minutes=1),
 
131
 
                         frozenset((token_second, token_end)))
 
132
 
    token_hour = Token(re.compile(r"(\d+)H"),
 
133
 
                       datetime.timedelta(hours=1),
 
134
 
                       frozenset((token_minute, token_end)))
 
135
 
    token_time = Token(re.compile(r"T"),
 
137
 
                       frozenset((token_hour, token_minute,
 
139
 
    token_day = Token(re.compile(r"(\d+)D"),
 
140
 
                      datetime.timedelta(days=1),
 
141
 
                      frozenset((token_time, token_end)))
 
142
 
    token_month = Token(re.compile(r"(\d+)M"),
 
143
 
                        datetime.timedelta(weeks=4),
 
144
 
                        frozenset((token_day, token_end)))
 
145
 
    token_year = Token(re.compile(r"(\d+)Y"),
 
146
 
                       datetime.timedelta(weeks=52),
 
147
 
                       frozenset((token_month, token_end)))
 
148
 
    token_week = Token(re.compile(r"(\d+)W"),
 
149
 
                       datetime.timedelta(weeks=1),
 
150
 
                       frozenset((token_end,)))
 
151
 
    token_duration = Token(re.compile(r"P"), None,
 
152
 
                           frozenset((token_year, token_month,
 
153
 
                                      token_day, token_time,
 
155
 
    # Define starting values
 
156
 
    value = datetime.timedelta() # Value so far
 
158
 
    followers = frozenset((token_duration,)) # Following valid tokens
 
159
 
    s = duration                # String left to parse
 
160
 
    # Loop until end token is found
 
161
 
    while found_token is not token_end:
 
162
 
        # Search for any currently valid tokens
 
163
 
        for token in followers:
 
164
 
            match = token.regexp.match(s)
 
165
 
            if match is not None:
 
167
 
                if token.value is not None:
 
168
 
                    # Value found, parse digits
 
169
 
                    factor = int(match.group(1), 10)
 
170
 
                    # Add to value so far
 
171
 
                    value += factor * token.value
 
172
 
                # Strip token from string
 
173
 
                s = token.regexp.sub("", s, 1)
 
176
 
                # Set valid next tokens
 
177
 
                followers = found_token.followers
 
180
 
            # No currently valid tokens were found
 
181
 
            raise ValueError("Invalid RFC 3339 duration")
 
186
83
def string_to_delta(interval):
 
187
84
    """Parse a string and return a datetime.timedelta
 
 
310
201
    parser.add_argument("--approval-duration",
 
311
202
                        help="Set duration of one client approval")
 
312
203
    parser.add_argument("-H", "--host", help="Set host for client")
 
313
 
    parser.add_argument("-s", "--secret",
 
314
 
                        type=argparse.FileType(mode="rb"),
 
 
204
    parser.add_argument("-s", "--secret", type=file,
 
315
205
                        help="Set password blob (file) for client")
 
316
206
    parser.add_argument("-A", "--approve", action="store_true",
 
317
207
                        help="Approve any current client request")
 
318
208
    parser.add_argument("-D", "--deny", action="store_true",
 
319
209
                        help="Deny any current client request")
 
320
 
    parser.add_argument("--check", action="store_true",
 
321
 
                        help="Run self-test")
 
322
210
    parser.add_argument("client", nargs="*", help="Client name")
 
323
211
    options = parser.parse_args()