aboutsummaryrefslogtreecommitdiffstats
path: root/main.py
blob: e39d82b31646c485b48ed13aa5e15342ad5f4a3b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import argparse
import os
import subprocess
import sys

import requests
from ruamel.yaml import YAML, scalarstring


class BlacklistImporter:
	def __init__(self, args):
		self.outfile = args.outfile
		self.dryrun = args.dryrun
		self.path = os.path.dirname(__file__)
		self.url = "https://raw.githubusercontent.com/JabberSPAM/blacklist/master/blacklist.txt"
		self.blacklist = ""
		self.change = False

	def request(self):
		"""
		determine if the download is required
		"""
		etag_path = "/".join([self.path, ".etag"])
		blacklist_path = "/".join([self.path, "blacklist.txt"])

		# check if etag header is present if not set local_etag to ""
		if os.path.isfile(etag_path):
			# catch special case were etag file is present and blacklist.txt is not
			if not os.path.isfile(blacklist_path):
				local_etag = ""
			else:
				# if both files are present continue normally
				with open(etag_path, "r") as local_file:
					local_etag = local_file.read()
		else:
			local_etag = ""

		with requests.Session() as s:
			# head request to check etag
			head = s.head(self.url)
			etag = head.headers['etag']

			# if etags match up or if the connection is not possible fall back to local cache
			if local_etag == etag or head.status_code != 200:
				# if local cache is present overwrite blacklist var
				if os.path.isfile(blacklist_path):
					with open(blacklist_path, "r", encoding="utf-8") as local_file:
						self.blacklist = local_file.read()

			# in any other case request a new file
			else:
				r = s.get(self.url)
				r.encoding = 'utf-8'
				local_etag = head.headers['etag']
				self.blacklist = r.content.decode()

				with open(blacklist_path, "w") as local_file:
					local_file.write(self.blacklist)

				with open(etag_path, 'w') as local_file:
					local_file.write(local_etag)

	def main(self):
		# first check if blacklist is updated
		self.request()

		# only output the selected outfile
		if self.dryrun:
			print("outfile selected: %s" % self.outfile)

		# blacklist processing
		self.process()

		# reload config if changes have been applied
		if self.change:
			# catch ejabberdctl missing
			if os.path.isfile('/usr/sbin/ejabberdctl'):
				subprocess.call(['/usr/sbin/ejabberdctl', 'reload_config'], shell=False)

			# report missing ejabberdctl reload_config
			else:
				print('/usr/sbin/ejabberdctl was not found', file=sys.stderr)
				print('blacklist changes have been applied\nejabberd config was not reloaded', file=sys.stderr)
				sys.exit(1)

	def process(self):
		"""
		function to build and compare the local yaml file to the remote file
		if the remote file is different, the local file gets overwritten
		"""
		# init new YAML variable
		local_file = YAML(typ="safe")

		# None catch
		if self.outfile is not None:
			# prevent FileNotFoundError on first run or file missing
			if os.path.isfile(self.outfile):
				local_file = local_file.load(open(self.outfile, "r", encoding="utf-8"))

		# blacklist frame
		remote_file = {
			"acl": {
				"spamblacklist": {
					"server": []
				}
			}
		}

		# build the blacklist with the given frame to compare to local blacklist
		for entry in self.blacklist.split():
			entry = scalarstring.DoubleQuotedScalarString(entry)
			remote_file["acl"]["spamblacklist"]["server"].append(entry)

		yml = YAML()
		yml.indent(offset=2)
		yml.default_flow_style = False

		# if dry-run true print expected content
		if self.dryrun:
			yml.dump(remote_file, sys.stdout)

		# only if the local_file and remote_file are unequal write new file
		elif local_file != remote_file:

			# prevent FileNotFoundError if self.outfile is not assigned
			if self.outfile is None:
				print("no outfile assigned", file=sys.stderr)
				print(parser.format_help(), file=sys.stderr)
				sys.exit(2)

			# proceed to update the defined outfile
			elif self.outfile is not None:
				self.change = True
				yml.dump(remote_file, open(self.outfile, "w"))

			# if that's impossible break and display help message
			else:
				print(parser.format_help(), file=sys.stderr)
				sys.exit(1)


if __name__ == "__main__":
	parser = argparse.ArgumentParser()
	parser.add_argument('-o', '--outfile', help='set path to output file', dest='outfile', default=None)
	parser.add_argument('-dr', '--dry-run', help='perform a dry run', action='store_true', dest='dryrun', default=False)
	args = parser.parse_args()

	# run
	BlacklistImporter(args).main()