5
5
import datetime
6
6
import os
7
7
import random
8
- import shutil
9
- import ssl
10
- import subprocess # nosec B404
8
+ import subprocess # nosec B404,S404
11
9
import sys
10
+ import shutil
11
+ import requests
12
12
import certifi
13
- import urllib3
14
13
import yaml
15
14
16
- __minimal__ = "{'verbose': 0}"
17
- __url__ = "duckduckgo.com"
18
- __version__ = "0.0.1"
19
-
20
- ARGS = None
21
- CONF = None
22
- EXCEPTION_STR = "Exception:"
23
- RESPONSE = None
24
- URL = None
25
- USER_AGENT = None
15
+ __version__ = "0.1.0"
26
16
27
17
28
18
def arguments ():
29
- """Command line arguments and help information."""
30
- global ARGS
31
-
19
+ """
20
+ Parse command line arguments using argparse module and return the parsed
21
+ arguments.
22
+ """
32
23
parser = argparse .ArgumentParser (
33
24
description = "tymely fetches HTTP-date over HTTPS and sets the system time" ,
34
25
epilog = "version: " + __version__ ,
@@ -43,167 +34,110 @@ def arguments():
43
34
action = "store_true" ,
44
35
)
45
36
46
- ARGS = parser .parse_args ()
47
-
37
+ return parser .parse_args ()
48
38
49
- def config ():
50
- """Returns CONF with yaml configuration files or default values."""
51
- global CONF
52
39
40
+ def config (args ):
41
+ """
42
+ Read and parse the configuration file specified in the command line arguments.
43
+ Return the configuration as a dictionary.
44
+ """
53
45
try :
54
- if ARGS .config :
55
- if not os .path .isfile (ARGS .config ):
56
- print (ARGS .config , "can't be found." )
46
+ if args .config :
47
+ if not os .path .isfile (args .config ):
48
+ print (args .config , "can't be found." )
57
49
sys .exit (1 )
58
50
else :
59
- with open (ARGS .config , "r" , encoding = "utf-8" ) as args_file :
60
- CONF = yaml .safe_load (args_file )
61
- conf_file = ARGS .config
51
+ with open (args .config , "r" , encoding = "utf-8" ) as args_file :
52
+ conf = yaml .safe_load (args_file )
62
53
else :
63
- CONF = yaml .safe_load (__minimal__ )
64
- conf_file = "None"
54
+ conf = {"verbose" : 0 }
65
55
66
- if CONF .get ("verbose" , 0 ):
56
+ if conf .get ("verbose" , 0 ):
67
57
print ("Verbose mode enabled" , file = sys .stdout )
68
- print ("Configuration file:" , conf_file , file = sys .stdout )
69
- print (CONF , file = sys .stdout )
70
-
71
- except IOError as exception_string :
72
- print (EXCEPTION_STR , str (exception_string ), file = sys .stderr )
73
- sys .exit (1 )
74
-
58
+ print ("Configuration file:" , args .config , file = sys .stdout )
59
+ print (conf , file = sys .stdout )
75
60
except KeyError as exception_string :
76
- print (EXCEPTION_STR , str (exception_string ), file = sys .stderr )
61
+ print ("Exception:" , str (exception_string ), file = sys .stderr )
77
62
sys .exit (1 )
78
-
79
63
except UnboundLocalError as exception_string :
80
- print (EXCEPTION_STR , str (exception_string ), file = sys .stderr )
64
+ print ("Exception:" , str (exception_string ), file = sys .stderr )
81
65
sys .exit (1 )
66
+ return conf
82
67
83
- return CONF
84
68
69
+ def get_site_and_agent (conf ):
70
+ """
71
+ Choose a site and user agent string from the configuration dictionary.
72
+ Return the site URL and user agent string.
73
+ """
74
+ user_agent = None
85
75
86
- def sites ():
87
- """Get site URLs from configuration file or use default."""
88
- global URL
76
+ if conf .get ("sites" ):
77
+ url = conf .get ("sites" , False )
78
+ url = random .SystemRandom ().choice (url )
79
+ else :
80
+ url = "duckduckgo.com"
89
81
90
- try :
91
- system_random = random .SystemRandom ()
92
- URL = CONF .get ("sites" , False )
93
- if URL :
94
- URL = system_random .choice (CONF ["sites" ])
95
- else :
96
- URL = __url__
82
+ tymely_agent = "tymely/" + __version__
83
+ user_agent = conf .get ("user_agents" , tymely_agent )
84
+ if user_agent != tymely_agent :
85
+ user_agent = random .SystemRandom ().choice (conf ["user_agents" ])
97
86
98
- except UnboundLocalError as exception_string :
99
- print (EXCEPTION_STR , str ( exception_string ) , file = sys .stderr )
100
- sys .exit ( 1 )
87
+ if conf . get ( "verbose" , 0 ) :
88
+ print ("URL:" , url , file = sys .stdout )
89
+ print ( "User agent:" , user_agent , file = sys .stdout )
101
90
102
- if CONF .get ("verbose" , 0 ):
103
- print ("URL:" , URL , file = sys .stdout )
91
+ return url , user_agent
104
92
105
- return URL
106
93
107
-
108
- def user_agents ():
109
- """Get user agents from configuration file or use default."""
110
- global USER_AGENT
94
+ def main ():
95
+ """
96
+ Main function that fetches the current date over HTTPS, sets the system time
97
+ if not in test mode, and prints the date if in test mode.
98
+ """
99
+ args = arguments ()
100
+ conf = config (args )
101
+ url , user_agent = get_site_and_agent (conf )
111
102
112
103
try :
113
- system_random = random .SystemRandom ()
114
-
115
- try :
116
- tymely_version = __version__
117
- tymely_agent = "tymely/" + tymely_version
118
-
119
- USER_AGENT = CONF .get ("user_agents" , tymely_agent )
120
- if USER_AGENT != tymely_agent :
121
- USER_AGENT = system_random .choice (CONF ["user_agents" ])
122
-
123
- except UnboundLocalError as exception_string :
124
- print (EXCEPTION_STR , str (exception_string ), file = sys .stderr )
125
- sys .exit (1 )
126
-
127
- except UnboundLocalError as exception_string :
128
- print (EXCEPTION_STR , str (exception_string ), file = sys .stderr )
129
- sys .exit (1 )
130
-
131
- if CONF .get ("verbose" , 0 ):
132
- print ("User agent:" , USER_AGENT , file = sys .stdout )
133
-
134
- return USER_AGENT
135
-
136
-
137
- def connection ():
138
- """Configure TLS and return the response."""
139
- global RESPONSE
140
-
141
- tls_cont = ssl .SSLContext (ssl .PROTOCOL_TLS_CLIENT )
142
- tls_cont .options |= ssl .OP_NO_SSLv2
143
- tls_cont .options |= ssl .OP_NO_SSLv3
144
- tls_cont .options |= ssl .OP_NO_TLSv1
145
- tls_cont .options |= ssl .OP_NO_TLSv1_1
146
-
147
- https = urllib3 .PoolManager (
148
- ssl_context = tls_cont ,
149
- cert_reqs = "CERT_REQUIRED" ,
150
- ca_certs = certifi .where (),
151
- retries = False ,
152
- timeout = 1.0 ,
153
- )
154
-
155
- try :
156
- RESPONSE = https .request (
157
- "HEAD" ,
158
- "https://" + URL ,
159
- headers = {"User-Agent" : USER_AGENT },
104
+ response = requests .head (
105
+ "https://" + url ,
106
+ headers = {"User-Agent" : user_agent },
107
+ verify = certifi .where (),
108
+ allow_redirects = True ,
109
+ timeout = 1.0 ,
160
110
)
161
111
162
- if CONF .get ("verbose" , 0 ):
163
- print ("Response headers:" , RESPONSE .headers , file = sys .stdout )
164
- print ("Verify_mode:" , tls_cont .verify_mode )
165
- print ("TLS context options:" , tls_cont .options )
166
- print (tls_cont .get_ciphers ())
167
-
168
- except urllib3 .exceptions .NewConnectionError :
169
- print ("Connection failed to" , URL , file = sys .stderr )
112
+ if conf .get ("verbose" , 0 ):
113
+ print ("Response headers:" , response .headers , file = sys .stdout )
114
+ print ("Verify_mode:" , requests .certs .where ())
115
+ except requests .exceptions .ConnectionError :
116
+ print ("Connection failed to" , url , file = sys .stderr )
170
117
sys .exit (1 )
171
118
172
- except UnboundLocalError as exception_string :
173
- print (EXCEPTION_STR , str (exception_string ), file = sys .stderr )
174
- sys .exit (1 )
175
-
176
- return RESPONSE
177
-
178
-
179
- def http_date ():
180
- """Return the http-date from URL using USER_AGENT."""
181
119
try :
182
- if RESPONSE . status != 200 :
183
- print ("Response code" , RESPONSE . status , "from" , URL , file = sys .stderr )
120
+ if response . status_code != 200 :
121
+ print ("Response code" , response . status_code , "from" , url , file = sys .stderr )
184
122
sys .exit (1 )
185
123
186
- date_str = RESPONSE .headers ["Date" ]
124
+ date_str = response .headers ["Date" ]
187
125
188
126
datetime .datetime .strptime (date_str , "%a, %d %b %Y %H:%M:%S GMT" ).timestamp ()
189
127
190
- if ARGS .test :
191
- print (date_str + " returned but not set" , file = sys .stdout )
128
+ if args .test :
129
+ print (f" { date_str } from { url } returned but not set" , file = sys .stdout )
192
130
else :
193
131
date_cmd = shutil .which ("date" )
194
132
subprocess .run (
195
- [date_cmd , "-s" , date_str ], shell = False , check = True # nosec B603
133
+ [date_cmd , "-s" , date_str ],
134
+ shell = False , # nosec B603
135
+ check = True ,
196
136
)
197
-
198
137
except UnboundLocalError as exception_string :
199
- print (EXCEPTION_STR , str (exception_string ), file = sys .stderr )
138
+ print ("Exception:" , str (exception_string ), file = sys .stderr )
200
139
sys .exit (1 )
201
140
202
141
203
142
if __name__ == "__main__" :
204
- arguments ()
205
- config ()
206
- sites ()
207
- user_agents ()
208
- connection ()
209
- http_date ()
143
+ main ()
0 commit comments