Archived
1
0

add unstaged changes

This commit is contained in:
Jeff Becker 2016-11-16 11:38:03 -05:00
parent c677cebde5
commit 3ab022f45d
5 changed files with 181 additions and 115 deletions

View File

@ -5,7 +5,7 @@ import hashlib
import re
import nacl.signing
from binascii import hexlify
from binascii import hexlify, unhexlify
from datetime import datetime
import time
@ -67,12 +67,20 @@ def createPost(newsgroup, ref, form, files, secretKey=None):
msg['Date'] = email.utils.format_datetime(datetime.now())
if ref and not msgid_valid(ref):
return None, "invalid reference: {}".format(ref)
if ref:
msg["References"] = ref
msg["Newsgroups"] = newsgroup
name = "Anonymous"
if 'name' in form:
name = form['name'] or name
if '#' in name:
parts = name.split('#')
secret = name[1+len(name):]
name = parts[0]
try:
assert len(unhexlify(secret.encode('ascii'))) == 32
except:
secret = hashlib.sha256(secret.encode('utf-8')).hexdigest()
secretKey = secret
msg["From"] = '{} <anon@django.nntpchan.tld>'.format(name)
if 'attachment' in files:
msg['Content-Type'] = 'multipart/mixed'
@ -93,6 +101,10 @@ def createPost(newsgroup, ref, form, files, secretKey=None):
m = '{}'.format(form['message'] or ' ')
msg.set_payload(m)
msg['Message-Id'] = '<{}${}@{}>'.format(randstr(5), int(time_int(datetime.now())), settings.FRONTEND_NAME)
if ref:
msg["References"] = ref
else:
msg["References"] = msg["Message-Id"]
if secretKey:
msg['Path'] = settings.FRONTEND_NAME
# sign
@ -103,7 +115,11 @@ def createPost(newsgroup, ref, form, files, secretKey=None):
body = msg.as_bytes()
h.update(body)
sig = hexlify(keypair.sign(h.digest()).signature).decode('ascii')
data = '''Content-Type: message/rfc822; charset=UTF-8
if ref:
data = 'References: ' + ref + '\n'
else:
data = ''
data += '''Content-Type: message/rfc822; charset=UTF-8
Message-ID: {}
Content-Transfer-Encoding: 8bit
Newsgroups: {}
@ -113,7 +129,7 @@ From: {}
Date: {}
Subject: {}
{}'''.format(msg["Message-ID"], newsgroup, pubkey, sig, msg["From"], msg["Date"], msg['Subject'], msg.as_string())
{}\n'''.format(msg["Message-ID"], msg["Refereces"], newsgroup, pubkey, sig, msg["From"], msg["Date"], msg['Subject'], msg.as_string())
data = data.encode('utf-8')
else:
data = msg.as_bytes()
@ -131,3 +147,17 @@ Subject: {}
if ref:
return ref, None
return None, None
def verify_message(pubkey, sig, payload):
h = hashlib.sha512()
h.update(payload[:-1])
d = h.digest()
sig = unhexlify(sig)
k = nacl.signing.VerifyKey(pubkey, nacl.signing.encoding.HexEncoder)
try:
k.verify(d, sig)
except:
return False
else:
return True

View File

@ -200,10 +200,23 @@ class ModView(generic.View, Postable):
return render(request, 'frontend/redirect.html', {'url' : reverse('frontend:mod'), 'msg' : msg } )
else:
# do mod action
return self.handle_mod_action(request)
return self.handle_mod_action(request, mod, action)
def handle_mod_action(self, request):
return render(request, 'frontend/redirect.html', {'url' : reverse('frontend:mod')} )
def handle_mod_action(self, request, mod, action):
msg = 'no action made'
if action is not None:
if action == 'delete':
# handle bulk delete
if 'posts' in request.POST:
body = '\ndelete '.join(request.POST['posts'].split())
sk = mod['sk']
_, err = util.createPost('ctl', '', {'message' : body }, {}, sk)
if err:
msg = 'error: {}'.format(err)
else:
msg = 'okay'
return render(request, 'frontend/redirect.html', {'url' : reverse('frontend:mod'), 'msg' : msg} )
def get(self, request):
mod = None

View File

@ -7,5 +7,4 @@
<input type="hidden" name="action" value="login"></input>
<input type="submit" value="login"></input>
</form>
{% endblock %}

View File

@ -5,6 +5,13 @@
<input type="hidden" name="action" value="logout"></input>
<input type="submit" value="logout"></input>
</form>
mod page
<form method="POST">
{% csrf_token %}
<input type="hidden" name="action" value="delete"></input>
<label for="bulk-delete"> bulk delete</label>
<textarea name="posts"></textarea>
<input type="submit" value="delete"></input>
</form>
{% endblock %}

View File

@ -3,7 +3,7 @@ from django.http import HttpResponse, HttpResponseNotAllowed, JsonResponse
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
from .frontend.models import Post, Attachment, Newsgroup
from .frontend.models import Post, Attachment, Newsgroup, ModPriv
from .frontend import util
from . import thumbnail
@ -28,112 +28,9 @@ def webhook(request):
"""
if request.method != 'POST':
return HttpResponseNotAllowed(['POST'])
try:
msg = email.message_from_bytes(request.body)
newsgroup = msg.get('Newsgroups')
if newsgroup is None:
raise Exception("no newsgroup specified")
if not util.newsgroup_valid(newsgroup):
raise Exception("invalid newsgroup name")
bump = True
group, created = Newsgroup.objects.get_or_create(name=newsgroup)
if created:
group.save()
if group.banned:
raise Exception("newsgroup is banned")
msgid = None
for h in ('Message-ID', 'Message-Id', 'MessageId', 'MessageID'):
if h in msg:
msgid = msg[h]
break
# check for sage
if 'X-Sage' in msg and msg['X-Sage'] == '1':
bump = False
if msgid is None:
raise Exception("no message id specified")
elif not util.msgid_valid(msgid):
raise Exception("invalid message id format: {}".format(msgid))
opmsgid = msgid
h = util.hashid(msgid)
atts = list()
ref = msg['References'] or ''
posted = util.time_int(email.utils.parsedate_to_datetime(msg['Date']))
if len(ref) > 0:
opmsgid = ref
f = msg['From'] or 'anon <anon@anon>'
name = email.utils.parseaddr(f)[0]
post, created = Post.objects.get_or_create(defaults={
'posthash': h,
'reference': ref,
'posted': posted,
'last_bumped': 0,
'name': name,
'subject': msg["Subject"] or '',
'newsgroup': group}, msgid=msgid)
if not created:
post.subject = msg["Subject"] or ''
post.name = name
post.posted = posted
m = ''
for part in msg.walk():
ctype = part.get_content_type()
if ctype.startswith("text/plain"):
m += '{} '.format(part.get_payload(decode=True).decode('utf-8'))
else:
payload = part.get_payload(decode=True)
if payload is None:
continue
filename = part.get_filename()
mtype = part.get_content_type()
ext = filename.split('.')[-1].lower()
fh = util.hashfile(bytes(payload))
fn = fh + '.' + ext
fname = os.path.join(settings.MEDIA_ROOT, fn)
if not os.path.exists(fname):
with open(fname, 'wb') as f:
f.write(payload)
tname = os.path.join(settings.MEDIA_ROOT, 'thumb-{}.jpg'.format(fn))
placeholder = os.path.join(settings.ASSETS_ROOT, 'placeholder.jpg')
if not os.path.exists(tname):
thumbnail.generate(fname, tname, placeholder)
att = Attachment(filehash=fh)
att.mimetype = mtype
att.filename = filename
att.save()
atts.append(att)
post.message = m
post.save()
for att in atts:
if post.has_attachment(att.filehash):
continue
post.attachments.add(att)
op, _ = Post.objects.get_or_create(defaults={
'posthash': util.hashid(opmsgid),
'reference': '',
'posted': 0,
'last_bumped': 0,
'name': 'OP',
'subject': 'OP Not Found',
'newsgroup': group}, msgid=opmsgid)
if bump:
op.bump(post.posted)
op.save()
process_message(msg)
except Exception as ex:
traceback.print_exc()
return JsonResponse({ 'error': '{}'.format(ex) })
@ -141,3 +38,123 @@ def webhook(request):
return JsonResponse({'posted': True})
def process_message(msg):
newsgroup = msg.get('Newsgroups')
if newsgroup is None:
raise Exception("no newsgroup specified")
if not util.newsgroup_valid(newsgroup):
raise Exception("invalid newsgroup name")
bump = True
group, created = Newsgroup.objects.get_or_create(name=newsgroup)
if created:
group.save()
if group.banned:
raise Exception("newsgroup is banned")
msgid = None
for h in ('Message-ID', 'Message-Id', 'MessageId', 'MessageID'):
if h in msg:
msgid = msg[h]
break
# check for sage
if 'X-Sage' in msg and msg['X-Sage'] == '1':
bump = False
if msgid is None:
raise Exception("no message id specified")
elif not util.msgid_valid(msgid):
raise Exception("invalid message id format: {}".format(msgid))
opmsgid = msgid
h = util.hashid(msgid)
atts = list()
ref = msg['References'] or ''
posted = util.time_int(email.utils.parsedate_to_datetime(msg['Date']))
if len(ref) > 0:
opmsgid = ref
f = msg['From'] or 'anon <anon@anon>'
name = email.utils.parseaddr(f)[0]
post, created = Post.objects.get_or_create(defaults={
'posthash': h,
'reference': ref,
'posted': posted,
'last_bumped': 0,
'name': name,
'subject': msg["Subject"] or '',
'newsgroup': group}, msgid=msgid)
if not created:
post.subject = msg["Subject"] or ''
post.name = name
post.posted = posted
m = ''
for part in msg.walk():
ctype = part.get_content_type()
print (ctype)
if ctype.startswith("text/plain"):
m += '{} '.format(part.get_payload(decode=True).decode('utf-8'))
elif ctype.startswith("message/rfc822"):
# signed message
payload = part.get_payload()
if payload is None:
raise Exception('invalid signed message, no body')
for inner in payload:
if not util.verify_message(msg["X-Pubkey-Ed25519"], msg['X-Signature-Ed25519-Sha512'], inner.as_bytes()):
raise Exception('invalid signed message, signature failed')
process_message(inner)
print('processed inner')
else:
payload = part.get_payload(decode=True)
if payload is None:
continue
filename = part.get_filename()
mtype = part.get_content_type()
ext = filename.split('.')[-1].lower()
fh = util.hashfile(bytes(payload))
fn = fh + '.' + ext
fname = os.path.join(settings.MEDIA_ROOT, fn)
if not os.path.exists(fname):
with open(fname, 'wb') as f:
f.write(payload)
tname = os.path.join(settings.MEDIA_ROOT, 'thumb-{}.jpg'.format(fn))
placeholder = os.path.join(settings.ASSETS_ROOT, 'placeholder.jpg')
if not os.path.exists(tname):
thumbnail.generate(fname, tname, placeholder)
att = Attachment(filehash=fh)
att.mimetype = mtype
att.filename = filename
att.save()
atts.append(att)
post.message = m
post.save()
for att in atts:
if post.has_attachment(att.filehash):
continue
post.attachments.add(att)
op, _ = Post.objects.get_or_create(defaults={
'posthash': util.hashid(opmsgid),
'reference': '',
'posted': 0,
'last_bumped': 0,
'name': 'OP',
'subject': 'OP Not Found',
'newsgroup': group}, msgid=opmsgid)
if bump:
op.bump(post.posted)
op.save()