Skip to content

Commit a200405

Browse files
committed
Merge pending pull requests
Commit 'pulls/origin/pr/6' (enhancements from pergamon) and commit 'pulls/origin/pr/7' (Python 3 support)
3 parents 457f4d5 + 337baa9 + 04d6e0d commit a200405

File tree

5 files changed

+74
-31
lines changed

5 files changed

+74
-31
lines changed

discuss/client.py

+22-2
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def autoreconnect(self, *args, **kwargs):
4343
class Client(object):
4444
"""Discuss client."""
4545

46-
def __init__(self, server, port = 2100, auth = True, timeout = None):
46+
def __init__(self, server, port = 2100, auth = True, timeout = None, RPCClient=RPCClient):
4747
self.rpc = RPCClient(server, port, auth, timeout)
4848
if auth and self.who_am_i().startswith("???@"):
4949
raise ProtocolError("Authentication to server failed")
@@ -65,6 +65,17 @@ def who_am_i(self):
6565
reply = self.rpc.request(request)
6666
return reply.read_string()
6767

68+
@autoreconnects
69+
def create_mtg(self, location, long_mtg_name, public):
70+
request = USPBlock(constants.CREATE_MTG)
71+
request.put_string(location)
72+
request.put_string(long_mtg_name)
73+
request.put_boolean(public)
74+
reply = self.rpc.request(request)
75+
result = reply.read_long_integer()
76+
if result != 0:
77+
raise DiscussError(result)
78+
6879
def close(self):
6980
"""Disconnect from the server."""
7081

@@ -300,6 +311,15 @@ def set_access(self, principal, modes):
300311
if result != 0:
301312
raise DiscussError(result)
302313

314+
def ensure_access(self, principal, modes):
315+
current = self.get_access(principal)
316+
self.set_access(principal, current+modes)
317+
318+
def remove_access(self, principal, modes):
319+
current = self.get_access(principal)
320+
new_modes = ''.join(c for c in current if not c in modes)
321+
self.set_access(principal, new_modes)
322+
303323
@autoreconnects
304324
def undelete_transaction(self, trn_number):
305325
"""Undelete the transaction by its number."""
@@ -340,7 +360,7 @@ def get_text(self):
340360
if result != 0:
341361
raise DiscussError(result)
342362

343-
return tfile.buffer
363+
return tfile.buffer.decode()
344364

345365
@autoreconnects
346366
def delete(self):

discuss/locator.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def _read_server_list(filename):
2828

2929
lines = map(remove_comments, lines) # comments
3030
lines = map(str.strip, lines) # whitespace
31-
lines = filter(lambda x: x, lines) # empty lines
31+
lines = [x for x in lines if x] # empty lines
3232

3333
return lines
3434
except IOError as err:

discuss/rpc.py

+30-7
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,10 @@
4242
#
4343

4444
import errno
45+
import fcntl
4546
import socket
4647
from struct import pack, unpack, calcsize
48+
import subprocess
4749
from functools import partial
4850

4951
from . import constants
@@ -91,9 +93,9 @@ def _get_krb5_ap_req(service, server):
9193
# bindings myself, but this is the yak I am not ready to shave at the
9294
# moment.
9395

94-
body_start = token_gssapi.find( chr(0x01) + chr(0x00) ) # 01 00 indicates that this is AP_REQ
95-
if token_gssapi[0] != chr(0x60) or \
96-
not (token_gssapi[2] == chr(0x06) or token_gssapi[4] == chr(0x06)) or \
96+
body_start = token_gssapi.find(b'\x01\x00') # 01 00 indicates that this is AP_REQ
97+
if token_gssapi[0:1] != b'\x60' or \
98+
not (token_gssapi[2:3] == b'\x06' or token_gssapi[4:5] == b'\x06') or \
9799
body_start == -1 or body_start < 8 or body_start > 64:
98100
raise ProtocolError("Invalid GSSAPI token provided by Python's Kerberos API")
99101

@@ -135,13 +137,13 @@ def put_string(self, s):
135137
# technical reasons from 1980s I do not really want to know. This works
136138
# out because input is null-terminated and wire format is has length
137139
# specified.
138-
encoded = s.replace("\r", "\r\0").replace("\n", "\r\n")
140+
encoded = s.encode().replace(b"\r", b"\r\0").replace(b"\n", b"\r\n")
139141
self.put_cardinal(len(encoded))
140142
self.buffer += encoded
141143

142144
# Padding
143145
if len(encoded) % 2 == 1:
144-
self.buffer += "\0"
146+
self.buffer += b"\0"
145147

146148
def send(self, sock):
147149
"""Sends the block over a socket."""
@@ -193,7 +195,7 @@ def read_string(self):
193195
omit = size + 1 if size % 2 ==1 else size # due to padding
194196
encoded, self.buffer = self.buffer[0:size], self.buffer[omit:]
195197

196-
return encoded.replace("\r\n", "\n").replace("\r\0", "\r")
198+
return encoded.replace(b"\r\n", b"\n").replace(b"\r\0", b"\r").decode()
197199

198200
@staticmethod
199201
def receive(sock):
@@ -272,7 +274,9 @@ def connect(self):
272274

273275
auth_block.put_cardinal(len(authenticator))
274276
for byte in authenticator:
275-
auth_block.put_cardinal(ord(byte))
277+
if str == bytes:
278+
byte = ord(byte)
279+
auth_block.put_cardinal(byte)
276280
else:
277281
auth_block.put_cardinal(0)
278282

@@ -314,3 +318,22 @@ def request(self, block):
314318
raise ProtocolError("Transport-level error")
315319
return reply
316320

321+
class RPCLocalClient(RPCClient):
322+
# Args are for compatibility with the remote RPC; most aren't used
323+
def __init__(self, server, port, auth, timeout):
324+
# Used as the id field on meeting objects, so copy it in
325+
self.server = server
326+
# port 2100 is the default port -> use the binary
327+
if port == 2100:
328+
port = '/usr/sbin/disserve'
329+
self.cmd = port
330+
331+
self.connect()
332+
self.make_wrapper()
333+
334+
def connect(self):
335+
pair = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
336+
subprocess.Popen([self.cmd], stdin=pair[1], close_fds=True)
337+
pair[1].close()
338+
fcntl.fcntl(pair[0].fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
339+
self.socket = pair[0]

setup.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#!/usr/bin/python
22

3-
from distutils.core import setup
3+
from setuptools import setup
44

55
setup(name='discuss',
6-
version='1.2',
6+
version='1.3',
77
description='Python client for Project Athena forum system',
88
author='Victor Vasiliev',
99
maintainer='Debathena Project',

tools/constants_gen.py

+19-19
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/python
2-
2+
from __future__ import print_function
33
# This file generates the constants from discuss sources.
44
# The first argument is path to those sources.
55
#
@@ -16,7 +16,7 @@
1616

1717
basepath = sys.argv[1]
1818
if not os.path.isdir(basepath):
19-
print "ERROR: the specified path is not a directory"
19+
print("ERROR: the specified path is not a directory")
2020
exit()
2121

2222
with open(basepath + "/ets/dsc_et.et", "r") as et_file_handler:
@@ -28,35 +28,35 @@
2828
header_match_entries = re.findall( r'#define ([A-Z0-9_]+)\s+(".+"|[0-9x\-]+)', header_file )
2929

3030
if not et_match_entries:
31-
print "ERROR: unable to parse dsc_et file correctly"
31+
print("ERROR: unable to parse dsc_et file correctly")
3232
exit()
3333
if not header_match_entries:
34-
print "ERROR: unable to parse rpc.h file correctly"
34+
print("ERROR: unable to parse rpc.h file correctly")
3535
exit()
3636

3737
##### Code file header #####
38-
print "# Discuss status codes and other constants, generated from discuss sources"
39-
print "#"
40-
print "# NOTE: this file was autogenerated from the following files:"
41-
print ""
38+
print("# Discuss status codes and other constants, generated from discuss sources")
39+
print("#")
40+
print("# NOTE: this file was autogenerated from the following files:")
41+
print("")
4242

4343
##### Discuss error codes #####
44-
print "# Error codes"
44+
print("# Error codes")
4545
cur_code = et_base
4646
for match in et_match_entries:
47-
print '%s = %s' % (match[0], repr(cur_code))
47+
print('%s = %s' % (match[0], repr(cur_code)))
4848
cur_code += 1
49-
print ""
49+
print("")
5050

51-
print "# Error code descriptions"
52-
print "errors = {"
51+
print("# Error code descriptions")
52+
print("errors = {")
5353
for match in et_match_entries:
54-
print ' %s : "%s",' % match
55-
print "}"
56-
print ""
54+
print(' %s : "%s",' % match)
55+
print("}")
56+
print("")
5757

5858
##### Constatns from rpc.h #####
59-
print "# Definitions from rpc.h"
59+
print("# Definitions from rpc.h")
6060
for match in header_match_entries:
61-
print '%s = %s' % match
62-
print ""
61+
print('%s = %s' % match)
62+
print("")

0 commit comments

Comments
 (0)