Move AGPL compliance code out of HTTPServer to a new agplcompliance.py file
[bitcoin:eloipool.git] / agplcompliance.py
1 # Eloipool - Python Bitcoin pool server
2 # Copyright (C) 2012-2014  Luke Dashjr <luke-jr+eloipool@utopios.org>
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as
6 # published by the Free Software Foundation, either version 3 of the
7 # License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU Affero General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 import logging
18 import os
19 import stat
20
21 # It is not legal to bypass or lie to this check. See LICENSE file for details.
22 try:
23         _srcdir = os.path.dirname(os.path.abspath(__file__))
24         if os.path.exists(_srcdir + '/.I_swear_that_I_am_Luke_Dashjr'):
25                 _SourceFiles = None
26         else:
27                 _SourceFiles = os.popen('cd \'%s\' && git ls-files' % (_srcdir,)).read().split('\n')
28                 try:
29                         _SourceFiles.remove('')
30                 except ValueError:
31                         pass
32                 if len(_SourceFiles) < 2:
33                         raise RuntimeError('Unknown error')
34                 _SourceFiles = tuple(x.encode('utf8') for x in _SourceFiles)
35                 _GitDesc = os.popen('cd \'%s\' && git describe --dirty --always' % (_srcdir,)).read().strip().encode('utf8')
36 except BaseException as e:
37         logging.getLogger('Licensing').critical('Error getting list of source files! AGPL requires this. To fix, be sure you are using git for Eloipool.\n' + traceback.format_exc())
38         import sys
39         sys.exit(1)
40
41 # For AGPL compliance, allow direct downloads of source code
42 def get_source(p):
43         if _SourceFiles is None:
44                 return None
45         if p == b'':
46                 # List of files
47                 body = b'<html><head><title>Source Code</title></head><body>\t\n'
48                 body += b'\t<a href="tar">(tar archive of all files)</a><br><br>\n'
49                 for f in _SourceFiles:
50                         body += b'\t<a href="' + f + b'">\n' + f + b'\n\t</a><br>\n'
51                 body += b'\t</body></html>\n'
52                 return ('text/html', body)
53         if p == b'tar':
54                 body = bytearray()
55                 dn = b'eloipool-' + _GitDesc + b'/'
56                 for f in _SourceFiles:
57                         fs = f.decode('utf8')
58                         fstat = os.lstat(fs)
59                         islink = stat.S_ISLNK(fstat.st_mode)
60                         if islink:
61                                 data = b''
62                                 link = os.readlink(f)
63                         else:
64                                 with open("%s/%s" % (_srcdir, fs), 'rb') as ff:
65                                         data = ff.read()
66                                 link = b''
67                         h = bytearray()
68                         f = dn + f
69                         h += f + bytes(max(0, 100 - len(f)))
70                         h += ('%07o' % (fstat.st_mode,)[-7:]).encode('utf8') + b'\0'
71                         h += bytes(16)
72                         h += ('%012o%012o' % (fstat.st_size, fstat.st_mtime)).encode('utf8')
73                         h += b'        '  # chksum
74                         h += b'2' if islink else b'0'
75                         h += link + bytes(max(0, 355 - len(link)))
76                         h[148:156] = ('%07o' % (sum(h),)).encode('utf8') + b'\0'
77                         body += h + data + bytes(512 - ((fstat.st_size % 512) or 512))
78                 return ('application/x-tar', body)
79         if p not in _SourceFiles:
80                 return None
81         ct = 'text/plain'
82         if p[-3:] == b'.py': ct = 'application/x-python'
83         elif p[-11:] == b'.py.example': ct = 'application/x-python'
84         p = p.decode('utf8')
85         with open("%s/%s" % (_srcdir, p), 'rb') as f:
86                 return (ct, f.read())