17db96d56Sopenharmony_ci#! /usr/bin/env python3
27db96d56Sopenharmony_ci
37db96d56Sopenharmony_ci# Copyright 1994 by Lance Ellinghouse
47db96d56Sopenharmony_ci# Cathedral City, California Republic, United States of America.
57db96d56Sopenharmony_ci#                        All Rights Reserved
67db96d56Sopenharmony_ci# Permission to use, copy, modify, and distribute this software and its
77db96d56Sopenharmony_ci# documentation for any purpose and without fee is hereby granted,
87db96d56Sopenharmony_ci# provided that the above copyright notice appear in all copies and that
97db96d56Sopenharmony_ci# both that copyright notice and this permission notice appear in
107db96d56Sopenharmony_ci# supporting documentation, and that the name of Lance Ellinghouse
117db96d56Sopenharmony_ci# not be used in advertising or publicity pertaining to distribution
127db96d56Sopenharmony_ci# of the software without specific, written prior permission.
137db96d56Sopenharmony_ci# LANCE ELLINGHOUSE DISCLAIMS ALL WARRANTIES WITH REGARD TO
147db96d56Sopenharmony_ci# THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
157db96d56Sopenharmony_ci# FITNESS, IN NO EVENT SHALL LANCE ELLINGHOUSE CENTRUM BE LIABLE
167db96d56Sopenharmony_ci# FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
177db96d56Sopenharmony_ci# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
187db96d56Sopenharmony_ci# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
197db96d56Sopenharmony_ci# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
207db96d56Sopenharmony_ci#
217db96d56Sopenharmony_ci# Modified by Jack Jansen, CWI, July 1995:
227db96d56Sopenharmony_ci# - Use binascii module to do the actual line-by-line conversion
237db96d56Sopenharmony_ci#   between ascii and binary. This results in a 1000-fold speedup. The C
247db96d56Sopenharmony_ci#   version is still 5 times faster, though.
257db96d56Sopenharmony_ci# - Arguments more compliant with python standard
267db96d56Sopenharmony_ci
277db96d56Sopenharmony_ci"""Implementation of the UUencode and UUdecode functions.
287db96d56Sopenharmony_ci
297db96d56Sopenharmony_ciencode(in_file, out_file [,name, mode], *, backtick=False)
307db96d56Sopenharmony_cidecode(in_file [, out_file, mode, quiet])
317db96d56Sopenharmony_ci"""
327db96d56Sopenharmony_ci
337db96d56Sopenharmony_ciimport binascii
347db96d56Sopenharmony_ciimport os
357db96d56Sopenharmony_ciimport sys
367db96d56Sopenharmony_ciimport warnings
377db96d56Sopenharmony_ci
387db96d56Sopenharmony_ciwarnings._deprecated(__name__, remove=(3, 13))
397db96d56Sopenharmony_ci
407db96d56Sopenharmony_ci__all__ = ["Error", "encode", "decode"]
417db96d56Sopenharmony_ci
427db96d56Sopenharmony_ciclass Error(Exception):
437db96d56Sopenharmony_ci    pass
447db96d56Sopenharmony_ci
457db96d56Sopenharmony_cidef encode(in_file, out_file, name=None, mode=None, *, backtick=False):
467db96d56Sopenharmony_ci    """Uuencode file"""
477db96d56Sopenharmony_ci    #
487db96d56Sopenharmony_ci    # If in_file is a pathname open it and change defaults
497db96d56Sopenharmony_ci    #
507db96d56Sopenharmony_ci    opened_files = []
517db96d56Sopenharmony_ci    try:
527db96d56Sopenharmony_ci        if in_file == '-':
537db96d56Sopenharmony_ci            in_file = sys.stdin.buffer
547db96d56Sopenharmony_ci        elif isinstance(in_file, str):
557db96d56Sopenharmony_ci            if name is None:
567db96d56Sopenharmony_ci                name = os.path.basename(in_file)
577db96d56Sopenharmony_ci            if mode is None:
587db96d56Sopenharmony_ci                try:
597db96d56Sopenharmony_ci                    mode = os.stat(in_file).st_mode
607db96d56Sopenharmony_ci                except AttributeError:
617db96d56Sopenharmony_ci                    pass
627db96d56Sopenharmony_ci            in_file = open(in_file, 'rb')
637db96d56Sopenharmony_ci            opened_files.append(in_file)
647db96d56Sopenharmony_ci        #
657db96d56Sopenharmony_ci        # Open out_file if it is a pathname
667db96d56Sopenharmony_ci        #
677db96d56Sopenharmony_ci        if out_file == '-':
687db96d56Sopenharmony_ci            out_file = sys.stdout.buffer
697db96d56Sopenharmony_ci        elif isinstance(out_file, str):
707db96d56Sopenharmony_ci            out_file = open(out_file, 'wb')
717db96d56Sopenharmony_ci            opened_files.append(out_file)
727db96d56Sopenharmony_ci        #
737db96d56Sopenharmony_ci        # Set defaults for name and mode
747db96d56Sopenharmony_ci        #
757db96d56Sopenharmony_ci        if name is None:
767db96d56Sopenharmony_ci            name = '-'
777db96d56Sopenharmony_ci        if mode is None:
787db96d56Sopenharmony_ci            mode = 0o666
797db96d56Sopenharmony_ci
807db96d56Sopenharmony_ci        #
817db96d56Sopenharmony_ci        # Remove newline chars from name
827db96d56Sopenharmony_ci        #
837db96d56Sopenharmony_ci        name = name.replace('\n','\\n')
847db96d56Sopenharmony_ci        name = name.replace('\r','\\r')
857db96d56Sopenharmony_ci
867db96d56Sopenharmony_ci        #
877db96d56Sopenharmony_ci        # Write the data
887db96d56Sopenharmony_ci        #
897db96d56Sopenharmony_ci        out_file.write(('begin %o %s\n' % ((mode & 0o777), name)).encode("ascii"))
907db96d56Sopenharmony_ci        data = in_file.read(45)
917db96d56Sopenharmony_ci        while len(data) > 0:
927db96d56Sopenharmony_ci            out_file.write(binascii.b2a_uu(data, backtick=backtick))
937db96d56Sopenharmony_ci            data = in_file.read(45)
947db96d56Sopenharmony_ci        if backtick:
957db96d56Sopenharmony_ci            out_file.write(b'`\nend\n')
967db96d56Sopenharmony_ci        else:
977db96d56Sopenharmony_ci            out_file.write(b' \nend\n')
987db96d56Sopenharmony_ci    finally:
997db96d56Sopenharmony_ci        for f in opened_files:
1007db96d56Sopenharmony_ci            f.close()
1017db96d56Sopenharmony_ci
1027db96d56Sopenharmony_ci
1037db96d56Sopenharmony_cidef decode(in_file, out_file=None, mode=None, quiet=False):
1047db96d56Sopenharmony_ci    """Decode uuencoded file"""
1057db96d56Sopenharmony_ci    #
1067db96d56Sopenharmony_ci    # Open the input file, if needed.
1077db96d56Sopenharmony_ci    #
1087db96d56Sopenharmony_ci    opened_files = []
1097db96d56Sopenharmony_ci    if in_file == '-':
1107db96d56Sopenharmony_ci        in_file = sys.stdin.buffer
1117db96d56Sopenharmony_ci    elif isinstance(in_file, str):
1127db96d56Sopenharmony_ci        in_file = open(in_file, 'rb')
1137db96d56Sopenharmony_ci        opened_files.append(in_file)
1147db96d56Sopenharmony_ci
1157db96d56Sopenharmony_ci    try:
1167db96d56Sopenharmony_ci        #
1177db96d56Sopenharmony_ci        # Read until a begin is encountered or we've exhausted the file
1187db96d56Sopenharmony_ci        #
1197db96d56Sopenharmony_ci        while True:
1207db96d56Sopenharmony_ci            hdr = in_file.readline()
1217db96d56Sopenharmony_ci            if not hdr:
1227db96d56Sopenharmony_ci                raise Error('No valid begin line found in input file')
1237db96d56Sopenharmony_ci            if not hdr.startswith(b'begin'):
1247db96d56Sopenharmony_ci                continue
1257db96d56Sopenharmony_ci            hdrfields = hdr.split(b' ', 2)
1267db96d56Sopenharmony_ci            if len(hdrfields) == 3 and hdrfields[0] == b'begin':
1277db96d56Sopenharmony_ci                try:
1287db96d56Sopenharmony_ci                    int(hdrfields[1], 8)
1297db96d56Sopenharmony_ci                    break
1307db96d56Sopenharmony_ci                except ValueError:
1317db96d56Sopenharmony_ci                    pass
1327db96d56Sopenharmony_ci        if out_file is None:
1337db96d56Sopenharmony_ci            # If the filename isn't ASCII, what's up with that?!?
1347db96d56Sopenharmony_ci            out_file = hdrfields[2].rstrip(b' \t\r\n\f').decode("ascii")
1357db96d56Sopenharmony_ci            if os.path.exists(out_file):
1367db96d56Sopenharmony_ci                raise Error(f'Cannot overwrite existing file: {out_file}')
1377db96d56Sopenharmony_ci            if (out_file.startswith(os.sep) or
1387db96d56Sopenharmony_ci                f'..{os.sep}' in out_file or (
1397db96d56Sopenharmony_ci                    os.altsep and
1407db96d56Sopenharmony_ci                    (out_file.startswith(os.altsep) or
1417db96d56Sopenharmony_ci                     f'..{os.altsep}' in out_file))
1427db96d56Sopenharmony_ci               ):
1437db96d56Sopenharmony_ci                raise Error(f'Refusing to write to {out_file} due to directory traversal')
1447db96d56Sopenharmony_ci        if mode is None:
1457db96d56Sopenharmony_ci            mode = int(hdrfields[1], 8)
1467db96d56Sopenharmony_ci        #
1477db96d56Sopenharmony_ci        # Open the output file
1487db96d56Sopenharmony_ci        #
1497db96d56Sopenharmony_ci        if out_file == '-':
1507db96d56Sopenharmony_ci            out_file = sys.stdout.buffer
1517db96d56Sopenharmony_ci        elif isinstance(out_file, str):
1527db96d56Sopenharmony_ci            fp = open(out_file, 'wb')
1537db96d56Sopenharmony_ci            os.chmod(out_file, mode)
1547db96d56Sopenharmony_ci            out_file = fp
1557db96d56Sopenharmony_ci            opened_files.append(out_file)
1567db96d56Sopenharmony_ci        #
1577db96d56Sopenharmony_ci        # Main decoding loop
1587db96d56Sopenharmony_ci        #
1597db96d56Sopenharmony_ci        s = in_file.readline()
1607db96d56Sopenharmony_ci        while s and s.strip(b' \t\r\n\f') != b'end':
1617db96d56Sopenharmony_ci            try:
1627db96d56Sopenharmony_ci                data = binascii.a2b_uu(s)
1637db96d56Sopenharmony_ci            except binascii.Error as v:
1647db96d56Sopenharmony_ci                # Workaround for broken uuencoders by /Fredrik Lundh
1657db96d56Sopenharmony_ci                nbytes = (((s[0]-32) & 63) * 4 + 5) // 3
1667db96d56Sopenharmony_ci                data = binascii.a2b_uu(s[:nbytes])
1677db96d56Sopenharmony_ci                if not quiet:
1687db96d56Sopenharmony_ci                    sys.stderr.write("Warning: %s\n" % v)
1697db96d56Sopenharmony_ci            out_file.write(data)
1707db96d56Sopenharmony_ci            s = in_file.readline()
1717db96d56Sopenharmony_ci        if not s:
1727db96d56Sopenharmony_ci            raise Error('Truncated input file')
1737db96d56Sopenharmony_ci    finally:
1747db96d56Sopenharmony_ci        for f in opened_files:
1757db96d56Sopenharmony_ci            f.close()
1767db96d56Sopenharmony_ci
1777db96d56Sopenharmony_cidef test():
1787db96d56Sopenharmony_ci    """uuencode/uudecode main program"""
1797db96d56Sopenharmony_ci
1807db96d56Sopenharmony_ci    import optparse
1817db96d56Sopenharmony_ci    parser = optparse.OptionParser(usage='usage: %prog [-d] [-t] [input [output]]')
1827db96d56Sopenharmony_ci    parser.add_option('-d', '--decode', dest='decode', help='Decode (instead of encode)?', default=False, action='store_true')
1837db96d56Sopenharmony_ci    parser.add_option('-t', '--text', dest='text', help='data is text, encoded format unix-compatible text?', default=False, action='store_true')
1847db96d56Sopenharmony_ci
1857db96d56Sopenharmony_ci    (options, args) = parser.parse_args()
1867db96d56Sopenharmony_ci    if len(args) > 2:
1877db96d56Sopenharmony_ci        parser.error('incorrect number of arguments')
1887db96d56Sopenharmony_ci        sys.exit(1)
1897db96d56Sopenharmony_ci
1907db96d56Sopenharmony_ci    # Use the binary streams underlying stdin/stdout
1917db96d56Sopenharmony_ci    input = sys.stdin.buffer
1927db96d56Sopenharmony_ci    output = sys.stdout.buffer
1937db96d56Sopenharmony_ci    if len(args) > 0:
1947db96d56Sopenharmony_ci        input = args[0]
1957db96d56Sopenharmony_ci    if len(args) > 1:
1967db96d56Sopenharmony_ci        output = args[1]
1977db96d56Sopenharmony_ci
1987db96d56Sopenharmony_ci    if options.decode:
1997db96d56Sopenharmony_ci        if options.text:
2007db96d56Sopenharmony_ci            if isinstance(output, str):
2017db96d56Sopenharmony_ci                output = open(output, 'wb')
2027db96d56Sopenharmony_ci            else:
2037db96d56Sopenharmony_ci                print(sys.argv[0], ': cannot do -t to stdout')
2047db96d56Sopenharmony_ci                sys.exit(1)
2057db96d56Sopenharmony_ci        decode(input, output)
2067db96d56Sopenharmony_ci    else:
2077db96d56Sopenharmony_ci        if options.text:
2087db96d56Sopenharmony_ci            if isinstance(input, str):
2097db96d56Sopenharmony_ci                input = open(input, 'rb')
2107db96d56Sopenharmony_ci            else:
2117db96d56Sopenharmony_ci                print(sys.argv[0], ': cannot do -t from stdin')
2127db96d56Sopenharmony_ci                sys.exit(1)
2137db96d56Sopenharmony_ci        encode(input, output)
2147db96d56Sopenharmony_ci
2157db96d56Sopenharmony_ciif __name__ == '__main__':
2167db96d56Sopenharmony_ci    test()
217