From b6191986ae08fb2553d1b76903a269cb22f63ae8 Mon Sep 17 00:00:00 2001 From: Alex Dehnert Date: Sun, 23 Feb 2014 21:17:24 -0500 Subject: [PATCH 1/7] Add local client mode The normal discuss clients will run disserve when connecting to a local discuss server, which is potentially handy for utilities running on the discuss server that don't have convenient access to a keytab. This adds support for that mode of operation to pydiscuss as well. --- discuss/client.py | 2 +- discuss/rpc.py | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/discuss/client.py b/discuss/client.py index ed2077e..28aa87c 100644 --- a/discuss/client.py +++ b/discuss/client.py @@ -43,7 +43,7 @@ def autoreconnect(self, *args, **kwargs): class Client(object): """Discuss client.""" - def __init__(self, server, port = 2100, auth = True, timeout = None): + def __init__(self, server, port = 2100, auth = True, timeout = None, RPCClient=RPCClient): self.rpc = RPCClient(server, port, auth, timeout) if auth and self.who_am_i().startswith("???@"): raise ProtocolError("Authentication to server failed") diff --git a/discuss/rpc.py b/discuss/rpc.py index 7c1532f..49dda7d 100644 --- a/discuss/rpc.py +++ b/discuss/rpc.py @@ -42,8 +42,10 @@ # import errno +import fcntl import socket from struct import pack, unpack, calcsize +import subprocess from functools import partial from . import constants @@ -314,3 +316,22 @@ def request(self, block): raise ProtocolError("Transport-level error") return reply +class RPCLocalClient(RPCClient): + # Args are for compatibility with the remote RPC; most aren't used + def __init__(self, server, port, auth, timeout): + # Used as the id field on meeting objects, so copy it in + self.server = server + # port 2100 is the default port -> use the binary + if port == 2100: + port = '/usr/sbin/disserve' + self.cmd = port + + self.connect() + self.make_wrapper() + + def connect(self): + pair = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM) + subprocess.Popen([self.cmd], stdin=pair[1], close_fds=True) + pair[1].close() + fcntl.fcntl(pair[0].fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC) + self.socket = pair[0] From bbdeffaa2ef25a2974d5667a87042727712dc2f5 Mon Sep 17 00:00:00 2001 From: Alex Dehnert Date: Mon, 24 Feb 2014 23:17:14 -0500 Subject: [PATCH 2/7] Add support for the CREATE_MTG discuss operation --- discuss/client.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/discuss/client.py b/discuss/client.py index 28aa87c..2894de0 100644 --- a/discuss/client.py +++ b/discuss/client.py @@ -65,6 +65,15 @@ def who_am_i(self): reply = self.rpc.request(request) return reply.read_string() + @autoreconnects + def create_mtg(self, location, long_mtg_name, public): + request = USPBlock(constants.CREATE_MTG) + request.put_string(location) + request.put_string(long_mtg_name) + request.put_boolean(public) + reply = self.rpc.request(request) + return reply.read_long_integer() + def close(self): """Disconnect from the server.""" From 44e1473ee2412b7291c86f83b6f246dd97b4c4ec Mon Sep 17 00:00:00 2001 From: Alex Dehnert Date: Wed, 26 Feb 2014 03:47:32 -0500 Subject: [PATCH 3/7] Convenience function to append to a user's ACL --- discuss/client.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/discuss/client.py b/discuss/client.py index 2894de0..54ea390 100644 --- a/discuss/client.py +++ b/discuss/client.py @@ -309,6 +309,10 @@ def set_access(self, principal, modes): if result != 0: raise DiscussError(result) + def ensure_access(self, principal, modes): + current = self.get_access(principal) + self.set_access(principal, current+modes) + @autoreconnects def undelete_transaction(self, trn_number): """Undelete the transaction by its number.""" From 122114cc2e55f7666b2fe9a2c168d6bc5e7b86e7 Mon Sep 17 00:00:00 2001 From: Alex Dehnert Date: Sat, 8 Mar 2014 18:09:52 -0500 Subject: [PATCH 4/7] Raise an exception when meeting creation fails --- discuss/client.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/discuss/client.py b/discuss/client.py index 54ea390..8106962 100644 --- a/discuss/client.py +++ b/discuss/client.py @@ -72,7 +72,9 @@ def create_mtg(self, location, long_mtg_name, public): request.put_string(long_mtg_name) request.put_boolean(public) reply = self.rpc.request(request) - return reply.read_long_integer() + result = reply.read_long_integer() + if result != 0: + raise DiscussError(result) def close(self): """Disconnect from the server.""" From 337baa9848153ebc38f5afe2c96825482de031cb Mon Sep 17 00:00:00 2001 From: Alex Dehnert Date: Sat, 8 Mar 2014 18:10:05 -0500 Subject: [PATCH 5/7] Add an API function for removing access too --- discuss/client.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/discuss/client.py b/discuss/client.py index 8106962..5e96367 100644 --- a/discuss/client.py +++ b/discuss/client.py @@ -315,6 +315,11 @@ def ensure_access(self, principal, modes): current = self.get_access(principal) self.set_access(principal, current+modes) + def remove_access(self, principal, modes): + current = self.get_access(principal) + new_modes = ''.join(c for c in current if not c in modes) + self.set_access(principal, new_modes) + @autoreconnects def undelete_transaction(self, trn_number): """Undelete the transaction by its number.""" From 04d6e0d8e5304f28e0e2ec353ade3d0f263097c5 Mon Sep 17 00:00:00 2001 From: Miriam Rittenberg Date: Sat, 29 Aug 2020 12:10:44 -0400 Subject: [PATCH 6/7] Make python3 compatible --- discuss/client.py | 2 +- discuss/locator.py | 2 +- discuss/rpc.py | 16 +++++++++------- setup.py | 4 ++-- tools/constants_gen.py | 38 +++++++++++++++++++------------------- 5 files changed, 32 insertions(+), 30 deletions(-) diff --git a/discuss/client.py b/discuss/client.py index ed2077e..aaafb4f 100644 --- a/discuss/client.py +++ b/discuss/client.py @@ -340,7 +340,7 @@ def get_text(self): if result != 0: raise DiscussError(result) - return tfile.buffer + return tfile.buffer.decode() @autoreconnects def delete(self): diff --git a/discuss/locator.py b/discuss/locator.py index f2a3045..9a7f9c1 100644 --- a/discuss/locator.py +++ b/discuss/locator.py @@ -28,7 +28,7 @@ def _read_server_list(filename): lines = map(remove_comments, lines) # comments lines = map(str.strip, lines) # whitespace - lines = filter(lambda x: x, lines) # empty lines + lines = [x for x in lines if x] # empty lines return lines except IOError as err: diff --git a/discuss/rpc.py b/discuss/rpc.py index 7c1532f..43369f0 100644 --- a/discuss/rpc.py +++ b/discuss/rpc.py @@ -91,9 +91,9 @@ def _get_krb5_ap_req(service, server): # bindings myself, but this is the yak I am not ready to shave at the # moment. - body_start = token_gssapi.find( chr(0x01) + chr(0x00) ) # 01 00 indicates that this is AP_REQ - if token_gssapi[0] != chr(0x60) or \ - not (token_gssapi[2] == chr(0x06) or token_gssapi[4] == chr(0x06)) or \ + body_start = token_gssapi.find(b'\x01\x00') # 01 00 indicates that this is AP_REQ + if token_gssapi[0:1] != b'\x60' or \ + not (token_gssapi[2:3] == b'\x06' or token_gssapi[4:5] == b'\x06') or \ body_start == -1 or body_start < 8 or body_start > 64: raise ProtocolError("Invalid GSSAPI token provided by Python's Kerberos API") @@ -135,13 +135,13 @@ def put_string(self, s): # technical reasons from 1980s I do not really want to know. This works # out because input is null-terminated and wire format is has length # specified. - encoded = s.replace("\r", "\r\0").replace("\n", "\r\n") + encoded = s.encode().replace(b"\r", b"\r\0").replace(b"\n", b"\r\n") self.put_cardinal(len(encoded)) self.buffer += encoded # Padding if len(encoded) % 2 == 1: - self.buffer += "\0" + self.buffer += b"\0" def send(self, sock): """Sends the block over a socket.""" @@ -193,7 +193,7 @@ def read_string(self): omit = size + 1 if size % 2 ==1 else size # due to padding encoded, self.buffer = self.buffer[0:size], self.buffer[omit:] - return encoded.replace("\r\n", "\n").replace("\r\0", "\r") + return encoded.replace(b"\r\n", b"\n").replace(b"\r\0", b"\r").decode() @staticmethod def receive(sock): @@ -272,7 +272,9 @@ def connect(self): auth_block.put_cardinal(len(authenticator)) for byte in authenticator: - auth_block.put_cardinal(ord(byte)) + if str == bytes: + byte = ord(byte) + auth_block.put_cardinal(byte) else: auth_block.put_cardinal(0) diff --git a/setup.py b/setup.py index afb85b8..7005451 100644 --- a/setup.py +++ b/setup.py @@ -1,9 +1,9 @@ #!/usr/bin/python -from distutils.core import setup +from setuptools import setup setup(name='discuss', - version='1.2', + version='1.3', description='Python client for Project Athena forum system', author='Victor Vasiliev', maintainer='Debathena Project', diff --git a/tools/constants_gen.py b/tools/constants_gen.py index e325fd0..1516ed0 100644 --- a/tools/constants_gen.py +++ b/tools/constants_gen.py @@ -1,5 +1,5 @@ #!/usr/bin/python - +from __future__ import print_function # This file generates the constants from discuss sources. # The first argument is path to those sources. # @@ -16,7 +16,7 @@ basepath = sys.argv[1] if not os.path.isdir(basepath): - print "ERROR: the specified path is not a directory" + print("ERROR: the specified path is not a directory") exit() with open(basepath + "/ets/dsc_et.et", "r") as et_file_handler: @@ -28,35 +28,35 @@ header_match_entries = re.findall( r'#define ([A-Z0-9_]+)\s+(".+"|[0-9x\-]+)', header_file ) if not et_match_entries: - print "ERROR: unable to parse dsc_et file correctly" + print("ERROR: unable to parse dsc_et file correctly") exit() if not header_match_entries: - print "ERROR: unable to parse rpc.h file correctly" + print("ERROR: unable to parse rpc.h file correctly") exit() ##### Code file header ##### -print "# Discuss status codes and other constants, generated from discuss sources" -print "#" -print "# NOTE: this file was autogenerated from the following files:" -print "" +print("# Discuss status codes and other constants, generated from discuss sources") +print("#") +print("# NOTE: this file was autogenerated from the following files:") +print("") ##### Discuss error codes ##### -print "# Error codes" +print("# Error codes") cur_code = et_base for match in et_match_entries: - print '%s = %s' % (match[0], repr(cur_code)) + print('%s = %s' % (match[0], repr(cur_code))) cur_code += 1 -print "" +print("") -print "# Error code descriptions" -print "errors = {" +print("# Error code descriptions") +print("errors = {") for match in et_match_entries: - print ' %s : "%s",' % match -print "}" -print "" + print(' %s : "%s",' % match) +print("}") +print("") ##### Constatns from rpc.h ##### -print "# Definitions from rpc.h" +print("# Definitions from rpc.h") for match in header_match_entries: - print '%s = %s' % match -print "" + print('%s = %s' % match) +print("") From f1c2728d6b08e4ba15bcc1ee5b3eefd84e7adbfa Mon Sep 17 00:00:00 2001 From: Alex Dehnert Date: Fri, 21 Feb 2025 02:54:14 -0500 Subject: [PATCH 7/7] Update Debian packaging for Python 3 --- debian/changelog | 10 ++++++++++ debian/compat | 2 +- debian/control | 9 ++++----- debian/python3-discuss.install | 2 ++ debian/rules | 4 +++- 5 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 debian/python3-discuss.install diff --git a/debian/changelog b/debian/changelog index 8f307b5..442d417 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,13 @@ +python-discuss (1.3-0debathena1) unstable; urgency=low + + * Release version 1.3. + * Add Python 3 support + * Update Debian packaging for Python 3 support + * Drop Python 2 support + * Add additional functionality for pergamon + + -- Alex Dehnert Fri, 21 Feb 2025 02:21:24 -0400 + python-discuss (1.2-0debathena3) unstable; urgency=low * Avoid building for Python 2.6 on distributions which support it diff --git a/debian/compat b/debian/compat index 7f8f011..f599e28 100644 --- a/debian/compat +++ b/debian/compat @@ -1 +1 @@ -7 +10 diff --git a/debian/control b/debian/control index 7002fee..3ba994c 100644 --- a/debian/control +++ b/debian/control @@ -1,14 +1,13 @@ Source: python-discuss Section: python -Priority: extra +Priority: optional Maintainer: Debathena Project -Build-Depends: debhelper (>= 7), python (>= 2.7) -X-Python-Version: 2.7 +Build-Depends: debhelper (>= 10), dh-python, python3-all Standards-Version: 3.9.4 -Package: python-discuss +Package: python3-discuss Architecture: all -Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends}, python (>= 2.7), python-kerberos +Depends: ${shlibs:Depends}, ${misc:Depends}, ${python3:Depends}, python3-kerberos Description: Python client for Project Athena forum system Pydiscuss provides a pure-Python implementation of discuss -- the forum protocol developed in late 80's and still used around MIT. diff --git a/debian/python3-discuss.install b/debian/python3-discuss.install new file mode 100644 index 0000000..9b58b6f --- /dev/null +++ b/debian/python3-discuss.install @@ -0,0 +1,2 @@ +etc/servers etc/discuss +etc/meetings.default etc/discuss diff --git a/debian/rules b/debian/rules index 2ebce13..4429223 100755 --- a/debian/rules +++ b/debian/rules @@ -9,5 +9,7 @@ # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 +export PYBUILD_NAME=discuss + %: - dh $@ --with python2 + dh $@ --with python3 --buildsystem=pybuild