104 lines
4.4 KiB
Python
Executable File
104 lines
4.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import os
|
|
import argparse
|
|
import textwrap
|
|
import tarfile
|
|
import io
|
|
import re
|
|
import pydoc
|
|
from urllib.request import urlopen
|
|
|
|
# TODO: non-txt format support? (i.e. PDF, HTML, etc.)
|
|
# TODO: search function? keyword or regex, display RFC number and title
|
|
|
|
def downloadRFC(destdir, rfcnum):
|
|
rfcnum = (str(rfcnum)).lower() # In case argparse interprets it as an int or it's entered in uppercase
|
|
# For when we implement format support:
|
|
# "plain" = raw text
|
|
# "html" = html text
|
|
# "pdf" = pdf text
|
|
# "x-gzip" is for a gzipped presumably tarball, but we use the "all" keyword for this.
|
|
filetypes = ['plain', 'html', 'pdf', 'x-gzip']
|
|
filext = {}
|
|
for t in filetypes:
|
|
filext[t] = t
|
|
filext['plain'] = 'txt'
|
|
filext['x-gzip'] = 'txt'
|
|
if rfcnum == 'all':
|
|
rfcuri = 'https://www.rfc-editor.org/rfc/tar/RFC-all.tar.gz'
|
|
rfctype = 'tar'
|
|
filetype = 'x-gzip'
|
|
else:
|
|
rfcuri = 'https://tools.ietf.org/rfc/rfc{0}.txt'.format(rfcnum)
|
|
rfctype = 'plain' # We'll make use of this later.
|
|
filetype = 'plain'
|
|
rfcdir = '{0}/{1}'.format(destdir, filext[filetype])
|
|
os.makedirs(rfcdir, exist_ok = True)
|
|
# And some minor fixes. We don't need .txt extension for plaintype. Remove if someone complains.
|
|
if filetype == 'plain':
|
|
rfcpath = '{0}/{1}'.format(rfcdir, rfcnum)
|
|
elif filetype == 'x-gzip':
|
|
# We need to handle this a special way, since it's a gzipped tarball.
|
|
rfcpath = rfcdir
|
|
else:
|
|
rfcpath = '{0}/{1}.{2}'.format(rfcdir, rfcnum, filext[filetype])
|
|
with urlopen(rfcuri) as rfc:
|
|
# Is this a single RFC, a release tarball, etc.
|
|
# Commented out for now until we implement multi-format support, as we have the 'all' keyword to help us.
|
|
#rfctype = rfc.response.info().get_content_subtype()
|
|
# Handle the tarball here.
|
|
if filetype == 'x-gzip':
|
|
content = io.BytesIO(rfc.read())
|
|
tarball = tarfile.open(fileobj = content)
|
|
for i in tarball.getnames():
|
|
filedest = '{0}/{1}'.format(rfcpath, re.sub('^rfc([0-9]+)\.txt$', '\g<1>', i))
|
|
if re.match('^rfc[0-9]+\.txt$', i):
|
|
with tarball.extractfile(i) as e:
|
|
with open(filedest, 'wb') as f:
|
|
f.write(e.read())
|
|
# We don't need to extract from the tarball, so we can just handle it as a plain read.
|
|
else:
|
|
content = rfc.read()
|
|
with open(rfcpath, 'wb') as f:
|
|
f.write(content)
|
|
|
|
def pageRFC(rfcnum):
|
|
rfcnum = (str(rfcnum)).lower() # In case argparse interprets it as an int or it's entered in uppercase
|
|
with urlopen('https://tools.ietf.org/rfc/rfc{0}.txt'.format(rfcnum)) as rfc:
|
|
pydoc.pager(rfc.read().decode('utf-8'))
|
|
|
|
def parseArgs():
|
|
args = argparse.ArgumentParser(description = 'RFC Downloader/Viewer',
|
|
epilog = 'TIP: this program has context-specific help. e.g. try "%(prog)s d -h"\nhttps://square-r00t.net/')
|
|
subparsers = args.add_subparsers(help = 'Operation to perform', dest = 'operation')
|
|
downloadargs = subparsers.add_parser('d', help = 'Download an RFC/RFCs.')
|
|
pagerargs = subparsers.add_parser('p', help = 'Print the RFC to the terminal.') # TODO: add -b/--browser? redirect to lynx for html paging?
|
|
downloadargs.add_argument('-d',
|
|
'--destination',
|
|
dest = 'destdir',
|
|
metavar = 'DESTINATION',
|
|
default = '/usr/local/share/doc/rfc',
|
|
help = 'The destination directory to save the RFC to. Will be created if it doesn\'t exist (assuming we have permissions). The default is /usr/local/share/doc/rfc/.')
|
|
downloadargs.add_argument(dest = 'rfcnum',
|
|
metavar = 'RFC',
|
|
default = 'all',
|
|
help = 'The RFC number. If the special value "all" is used, then ALL of the published RFCs will be fetched.')
|
|
pagerargs.add_argument(dest = 'rfcnum',
|
|
metavar = 'RFC',
|
|
help = 'The RFC number.')
|
|
return(args)
|
|
|
|
def main():
|
|
argsin = parseArgs()
|
|
args = argsin.parse_args()
|
|
if args.operation == 'd':
|
|
downloadRFC(args.destdir, args.rfcnum)
|
|
elif args.operation == 'p':
|
|
pageRFC(args.rfcnum)
|
|
else:
|
|
argsin.print_help()
|
|
|
|
if __name__ == '__main__':
|
|
main()
|