use markdown2, add forced login. Added breadcrumbs
This commit is contained in:
13
captcha.py
13
captcha.py
@@ -1,20 +1,25 @@
|
|||||||
from random import randint
|
from random import randint
|
||||||
import hashlib, base64
|
import hashlib, base64
|
||||||
|
|
||||||
|
|
||||||
# FIXME hash can be reused
|
# FIXME hash can be reused
|
||||||
def generate(key):
|
def generate(key):
|
||||||
'''
|
"""
|
||||||
Generate a simple CAPTCHA.
|
Generate a simple CAPTCHA.
|
||||||
It is based on a simple math expression which stops the simplest of bots.
|
It is based on a simple math expression which stops the simplest of bots.
|
||||||
'''
|
"""
|
||||||
# The parameters are chosen such that they are simple to solve on paper.
|
# The parameters are chosen such that they are simple to solve on paper.
|
||||||
a = randint(1, 10)
|
a = randint(1, 10)
|
||||||
b = randint(1, 10)
|
b = randint(1, 10)
|
||||||
c = randint(10, 20)
|
c = randint(10, 20)
|
||||||
return f'{a} * {b} + {c} = ', _hash_answer(key, str(a * b + c))
|
return f"{a} * {b} + {c} = ", _hash_answer(key, str(a * b + c))
|
||||||
|
|
||||||
|
|
||||||
def verify(key, answer, hash):
|
def verify(key, answer, hash):
|
||||||
return _hash_answer(key, answer) == hash
|
return _hash_answer(key, answer) == hash
|
||||||
|
|
||||||
|
|
||||||
def _hash_answer(key, answer):
|
def _hash_answer(key, answer):
|
||||||
return base64.b64encode(hashlib.sha256((key + answer).encode('utf-8')).digest()).decode('ascii')
|
return base64.b64encode(
|
||||||
|
hashlib.sha256((key + answer).encode("utf-8")).digest()
|
||||||
|
).decode("ascii")
|
||||||
|
|||||||
427
db/sqlite.py
427
db/sqlite.py
@@ -1,18 +1,25 @@
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
|
|
||||||
class DB:
|
class DB:
|
||||||
def __init__(self, conn):
|
def __init__(self, conn):
|
||||||
self.conn = conn
|
self.conn = conn
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_config(self):
|
def get_config(self):
|
||||||
return self._db().execute('''
|
return (
|
||||||
select version, name, description, secret_key, captcha_key, registration_enabled from config
|
self._db()
|
||||||
'''
|
.execute(
|
||||||
).fetchone()
|
"""
|
||||||
|
select version, name, description, secret_key, captcha_key, registration_enabled, login_required from config
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
.fetchone()
|
||||||
|
)
|
||||||
|
|
||||||
def get_forums(self):
|
def get_forums(self):
|
||||||
return self._db().execute('''
|
return self._db().execute(
|
||||||
|
"""
|
||||||
select f.forum_id, name, description, thread_id, title, update_time
|
select f.forum_id, name, description, thread_id, title, update_time
|
||||||
from forums f
|
from forums f
|
||||||
left join threads t
|
left join threads t
|
||||||
@@ -23,20 +30,41 @@ class DB:
|
|||||||
order by update_time desc
|
order by update_time desc
|
||||||
limit 1
|
limit 1
|
||||||
)
|
)
|
||||||
'''
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_forum(self, forum_id):
|
def get_forum(self, forum_id):
|
||||||
return self._db().execute('''
|
return (
|
||||||
|
self._db()
|
||||||
|
.execute(
|
||||||
|
"""
|
||||||
select name, description
|
select name, description
|
||||||
from forums
|
from forums
|
||||||
where forum_id = ?
|
where forum_id = ?
|
||||||
''',
|
""",
|
||||||
(forum_id,)
|
(forum_id,),
|
||||||
).fetchone()
|
)
|
||||||
|
.fetchone()
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_thread_forum(self, thread_id):
|
||||||
|
""" Returns forum_id of a thread """
|
||||||
|
return (
|
||||||
|
self._db()
|
||||||
|
.execute(
|
||||||
|
"""
|
||||||
|
select forum_id
|
||||||
|
from threads
|
||||||
|
where thread_id = ?
|
||||||
|
""",
|
||||||
|
(thread_id,),
|
||||||
|
)
|
||||||
|
.fetchone()[0]
|
||||||
|
)
|
||||||
|
|
||||||
def get_threads(self, forum_id, offset, limit, user_id):
|
def get_threads(self, forum_id, offset, limit, user_id):
|
||||||
return self._db().execute('''
|
return self._db().execute(
|
||||||
|
"""
|
||||||
select
|
select
|
||||||
t.thread_id,
|
t.thread_id,
|
||||||
title,
|
title,
|
||||||
@@ -70,20 +98,22 @@ class DB:
|
|||||||
order by t.update_time desc
|
order by t.update_time desc
|
||||||
limit ?
|
limit ?
|
||||||
offset ?
|
offset ?
|
||||||
''',
|
""",
|
||||||
(forum_id, user_id, limit, offset)
|
(forum_id, user_id, limit, offset),
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_thread(self, thread):
|
def get_thread(self, thread):
|
||||||
db = self._db()
|
db = self._db()
|
||||||
title, text, author, author_id, create_time, modify_time, hidden = db.execute('''
|
title, text, author, author_id, create_time, modify_time, hidden, forum_id = db.execute(
|
||||||
select title, text, name, author_id, create_time, modify_time, hidden
|
"""
|
||||||
|
select title, text, name, author_id, create_time, modify_time, hidden, forum_id
|
||||||
from threads, users
|
from threads, users
|
||||||
where thread_id = ? and author_id = user_id
|
where thread_id = ? and author_id = user_id
|
||||||
''',
|
""",
|
||||||
(thread,)
|
(thread,),
|
||||||
).fetchone()
|
).fetchone()
|
||||||
comments = db.execute('''
|
comments = db.execute(
|
||||||
|
"""
|
||||||
select
|
select
|
||||||
comment_id,
|
comment_id,
|
||||||
parent_id,
|
parent_id,
|
||||||
@@ -97,59 +127,91 @@ class DB:
|
|||||||
left join users
|
left join users
|
||||||
on author_id = user_id
|
on author_id = user_id
|
||||||
where thread_id = ?
|
where thread_id = ?
|
||||||
''',
|
""",
|
||||||
(thread,)
|
(thread,),
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
title,
|
||||||
|
text,
|
||||||
|
author,
|
||||||
|
author_id,
|
||||||
|
create_time,
|
||||||
|
modify_time,
|
||||||
|
comments,
|
||||||
|
hidden,
|
||||||
|
forum_id
|
||||||
)
|
)
|
||||||
return title, text, author, author_id, create_time, modify_time, comments, hidden
|
|
||||||
|
|
||||||
def get_thread_title(self, thread_id):
|
def get_thread_title(self, thread_id):
|
||||||
return self._db().execute('''
|
return (
|
||||||
|
self._db()
|
||||||
|
.execute(
|
||||||
|
"""
|
||||||
select title
|
select title
|
||||||
from threads
|
from threads
|
||||||
where thread_id = ?
|
where thread_id = ?
|
||||||
''',
|
""",
|
||||||
(thread_id,)
|
(thread_id,),
|
||||||
).fetchone()
|
)
|
||||||
|
.fetchone()
|
||||||
|
)
|
||||||
|
|
||||||
def get_thread_title_text(self, thread_id):
|
def get_thread_title_text(self, thread_id):
|
||||||
return self._db().execute('''
|
return (
|
||||||
|
self._db()
|
||||||
|
.execute(
|
||||||
|
"""
|
||||||
select title, text
|
select title, text
|
||||||
from threads
|
from threads
|
||||||
where thread_id = ?
|
where thread_id = ?
|
||||||
''',
|
""",
|
||||||
(thread_id,)
|
(thread_id,),
|
||||||
).fetchone()
|
)
|
||||||
|
.fetchone()
|
||||||
|
)
|
||||||
|
|
||||||
def get_recent_threads(self, limit):
|
def get_recent_threads(self, limit):
|
||||||
return self._db().execute('''
|
return self._db().execute(
|
||||||
|
"""
|
||||||
select thread_id, title, modify_date
|
select thread_id, title, modify_date
|
||||||
from threads
|
from threads
|
||||||
order by modify_date
|
order by modify_date
|
||||||
limit ?
|
limit ?
|
||||||
''',
|
""",
|
||||||
(limit,)
|
(limit,),
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_comment(self, comment_id):
|
def get_comment(self, comment_id):
|
||||||
return self._db().execute('''
|
return (
|
||||||
|
self._db()
|
||||||
|
.execute(
|
||||||
|
"""
|
||||||
select title, c.text
|
select title, c.text
|
||||||
from comments c, threads t
|
from comments c, threads t
|
||||||
where comment_id = ? and c.thread_id = t.thread_id
|
where comment_id = ? and c.thread_id = t.thread_id
|
||||||
''',
|
""",
|
||||||
(comment_id,)
|
(comment_id,),
|
||||||
).fetchone()
|
)
|
||||||
|
.fetchone()
|
||||||
|
)
|
||||||
|
|
||||||
def get_subcomments(self, comment_id):
|
def get_subcomments(self, comment_id):
|
||||||
db = self._db()
|
db = self._db()
|
||||||
thread_id, parent_id, title = db.execute('''
|
thread_id, parent_id, title = db.execute(
|
||||||
|
"""
|
||||||
select threads.thread_id, parent_id, title
|
select threads.thread_id, parent_id, title
|
||||||
from threads, comments
|
from threads, comments
|
||||||
where comment_id = ? and threads.thread_id = comments.thread_id
|
where comment_id = ? and threads.thread_id = comments.thread_id
|
||||||
''',
|
""",
|
||||||
(comment_id,)
|
(comment_id,),
|
||||||
).fetchone()
|
).fetchone()
|
||||||
# Recursive CTE, see https://www.sqlite.org/lang_with.html
|
# Recursive CTE, see https://www.sqlite.org/lang_with.html
|
||||||
return thread_id, parent_id, title, db.execute('''
|
return (
|
||||||
|
thread_id,
|
||||||
|
parent_id,
|
||||||
|
title,
|
||||||
|
db.execute(
|
||||||
|
"""
|
||||||
with recursive
|
with recursive
|
||||||
descendant_of(id) as (
|
descendant_of(id) as (
|
||||||
select comment_id from comments where comment_id = ?
|
select comment_id from comments where comment_id = ?
|
||||||
@@ -171,112 +233,148 @@ class DB:
|
|||||||
users
|
users
|
||||||
where id = comment_id
|
where id = comment_id
|
||||||
and user_id = author_id
|
and user_id = author_id
|
||||||
''',
|
""",
|
||||||
(comment_id,)
|
(comment_id,),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_user_password(self, username):
|
def get_user_password(self, username):
|
||||||
return self._db().execute('''
|
return (
|
||||||
|
self._db()
|
||||||
|
.execute(
|
||||||
|
"""
|
||||||
select user_id, password
|
select user_id, password
|
||||||
from users
|
from users
|
||||||
where name = lower(?)
|
where name = lower(?)
|
||||||
''',
|
""",
|
||||||
(username,)
|
(username,),
|
||||||
).fetchone()
|
)
|
||||||
|
.fetchone()
|
||||||
|
)
|
||||||
|
|
||||||
def get_user_password_by_id(self, user_id):
|
def get_user_password_by_id(self, user_id):
|
||||||
return self._db().execute('''
|
return (
|
||||||
|
self._db()
|
||||||
|
.execute(
|
||||||
|
"""
|
||||||
select password
|
select password
|
||||||
from users
|
from users
|
||||||
where user_id = ?
|
where user_id = ?
|
||||||
''',
|
""",
|
||||||
(user_id,)
|
(user_id,),
|
||||||
).fetchone()
|
)
|
||||||
|
.fetchone()
|
||||||
|
)
|
||||||
|
|
||||||
def set_user_password(self, user_id, password):
|
def set_user_password(self, user_id, password):
|
||||||
return self.change_one('''
|
return self.change_one(
|
||||||
|
"""
|
||||||
update users
|
update users
|
||||||
set password = ?
|
set password = ?
|
||||||
where user_id = ?
|
where user_id = ?
|
||||||
''',
|
""",
|
||||||
(password, user_id)
|
(password, user_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_user_public_info(self, user_id):
|
def get_user_public_info(self, user_id):
|
||||||
return self._db().execute('''
|
return (
|
||||||
|
self._db()
|
||||||
|
.execute(
|
||||||
|
"""
|
||||||
select name, about, banned_until
|
select name, about, banned_until
|
||||||
from users
|
from users
|
||||||
where user_id = ?
|
where user_id = ?
|
||||||
''',
|
""",
|
||||||
(user_id,)
|
(user_id,),
|
||||||
).fetchone()
|
)
|
||||||
|
.fetchone()
|
||||||
|
)
|
||||||
|
|
||||||
def get_user_private_info(self, user_id):
|
def get_user_private_info(self, user_id):
|
||||||
return self._db().execute('''
|
return (
|
||||||
|
self._db()
|
||||||
|
.execute(
|
||||||
|
"""
|
||||||
select about
|
select about
|
||||||
from users
|
from users
|
||||||
where user_id = ?
|
where user_id = ?
|
||||||
''',
|
""",
|
||||||
(user_id,)
|
(user_id,),
|
||||||
).fetchone()
|
)
|
||||||
|
.fetchone()
|
||||||
|
)
|
||||||
|
|
||||||
def set_user_private_info(self, user_id, about):
|
def set_user_private_info(self, user_id, about):
|
||||||
db = self._db()
|
db = self._db()
|
||||||
db.execute('''
|
db.execute(
|
||||||
|
"""
|
||||||
update users
|
update users
|
||||||
set about = ?
|
set about = ?
|
||||||
where user_id = ?
|
where user_id = ?
|
||||||
''',
|
""",
|
||||||
(about, user_id)
|
(about, user_id),
|
||||||
)
|
)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
def get_user_name_role_banned(self, user_id):
|
def get_user_name_role_banned(self, user_id):
|
||||||
return self._db().execute('''
|
return (
|
||||||
|
self._db()
|
||||||
|
.execute(
|
||||||
|
"""
|
||||||
select name, role, banned_until
|
select name, role, banned_until
|
||||||
from users
|
from users
|
||||||
where user_id = ?
|
where user_id = ?
|
||||||
''',
|
""",
|
||||||
(user_id,)
|
(user_id,),
|
||||||
).fetchone()
|
)
|
||||||
|
.fetchone()
|
||||||
|
)
|
||||||
|
|
||||||
def get_user_name(self, user_id):
|
def get_user_name(self, user_id):
|
||||||
return self._db().execute('''
|
return (
|
||||||
|
self._db()
|
||||||
|
.execute(
|
||||||
|
"""
|
||||||
select name
|
select name
|
||||||
from users
|
from users
|
||||||
where user_id = ?
|
where user_id = ?
|
||||||
''',
|
""",
|
||||||
(user_id,)
|
(user_id,),
|
||||||
).fetchone()
|
)
|
||||||
|
.fetchone()
|
||||||
|
)
|
||||||
|
|
||||||
def add_thread(self, author_id, forum_id, title, text, time):
|
def add_thread(self, author_id, forum_id, title, text, time):
|
||||||
db = self._db()
|
db = self._db()
|
||||||
c = db.cursor()
|
c = db.cursor()
|
||||||
c.execute('''
|
c.execute(
|
||||||
|
"""
|
||||||
insert into threads (author_id, forum_id, title, text,
|
insert into threads (author_id, forum_id, title, text,
|
||||||
create_time, modify_time, update_time)
|
create_time, modify_time, update_time)
|
||||||
select ?, ?, ?, ?, ?, ?, ?
|
select ?, ?, ?, ?, ?, ?, ?
|
||||||
from users
|
from users
|
||||||
where user_id = ? and banned_until < ?
|
where user_id = ? and banned_until < ?
|
||||||
''',
|
""",
|
||||||
(author_id, forum_id, title, text, time, time, time, author_id, time)
|
(author_id, forum_id, title, text, time, time, time, author_id, time),
|
||||||
)
|
)
|
||||||
rowid = c.lastrowid
|
rowid = c.lastrowid
|
||||||
if rowid is None:
|
if rowid is None:
|
||||||
return None
|
return None
|
||||||
db.commit()
|
db.commit()
|
||||||
return db.execute('''
|
return db.execute(
|
||||||
|
"""
|
||||||
select thread_id
|
select thread_id
|
||||||
from threads
|
from threads
|
||||||
where rowid = ?
|
where rowid = ?
|
||||||
''',
|
""",
|
||||||
(rowid,)
|
(rowid,),
|
||||||
).fetchone()
|
).fetchone()
|
||||||
|
|
||||||
def delete_thread(self, user_id, thread_id):
|
def delete_thread(self, user_id, thread_id):
|
||||||
db = self._db()
|
db = self._db()
|
||||||
c = db.cursor()
|
c = db.cursor()
|
||||||
c.execute('''
|
c.execute(
|
||||||
|
"""
|
||||||
delete
|
delete
|
||||||
from threads
|
from threads
|
||||||
-- 1 = moderator, 2 = admin
|
-- 1 = moderator, 2 = admin
|
||||||
@@ -284,8 +382,8 @@ class DB:
|
|||||||
author_id = ?
|
author_id = ?
|
||||||
or (select 1 from users where user_id = ? and (role = 1 or role = 2))
|
or (select 1 from users where user_id = ? and (role = 1 or role = 2))
|
||||||
)
|
)
|
||||||
''',
|
""",
|
||||||
(thread_id, user_id, user_id)
|
(thread_id, user_id, user_id),
|
||||||
)
|
)
|
||||||
db.commit()
|
db.commit()
|
||||||
return c.rowcount > 0
|
return c.rowcount > 0
|
||||||
@@ -293,7 +391,8 @@ class DB:
|
|||||||
def delete_comment(self, user_id, comment_id):
|
def delete_comment(self, user_id, comment_id):
|
||||||
db = self._db()
|
db = self._db()
|
||||||
c = db.cursor()
|
c = db.cursor()
|
||||||
c.execute('''
|
c.execute(
|
||||||
|
"""
|
||||||
delete
|
delete
|
||||||
from comments
|
from comments
|
||||||
where comment_id = ?
|
where comment_id = ?
|
||||||
@@ -304,8 +403,8 @@ class DB:
|
|||||||
)
|
)
|
||||||
-- Don't allow deleting comments with children
|
-- Don't allow deleting comments with children
|
||||||
and (select 1 from comments where parent_id = ?) is null
|
and (select 1 from comments where parent_id = ?) is null
|
||||||
''',
|
""",
|
||||||
(comment_id, user_id, user_id, comment_id)
|
(comment_id, user_id, user_id, comment_id),
|
||||||
)
|
)
|
||||||
db.commit()
|
db.commit()
|
||||||
return c.rowcount > 0
|
return c.rowcount > 0
|
||||||
@@ -313,21 +412,23 @@ class DB:
|
|||||||
def add_comment_to_thread(self, thread_id, author_id, text, time):
|
def add_comment_to_thread(self, thread_id, author_id, text, time):
|
||||||
db = self._db()
|
db = self._db()
|
||||||
c = db.cursor()
|
c = db.cursor()
|
||||||
c.execute('''
|
c.execute(
|
||||||
|
"""
|
||||||
insert into comments(thread_id, author_id, text, create_time, modify_time)
|
insert into comments(thread_id, author_id, text, create_time, modify_time)
|
||||||
select ?, ?, ?, ?, ?
|
select ?, ?, ?, ?, ?
|
||||||
from threads, users
|
from threads, users
|
||||||
where thread_id = ? and user_id = ? and banned_until < ?
|
where thread_id = ? and user_id = ? and banned_until < ?
|
||||||
''',
|
""",
|
||||||
(thread_id, author_id, text, time, time, thread_id, author_id, time)
|
(thread_id, author_id, text, time, time, thread_id, author_id, time),
|
||||||
)
|
)
|
||||||
if c.rowcount > 0:
|
if c.rowcount > 0:
|
||||||
c.execute('''
|
c.execute(
|
||||||
|
"""
|
||||||
update threads
|
update threads
|
||||||
set update_time = ?
|
set update_time = ?
|
||||||
where thread_id = ?
|
where thread_id = ?
|
||||||
''',
|
""",
|
||||||
(time, thread_id)
|
(time, thread_id),
|
||||||
)
|
)
|
||||||
db.commit()
|
db.commit()
|
||||||
return True
|
return True
|
||||||
@@ -336,16 +437,18 @@ class DB:
|
|||||||
def add_comment_to_comment(self, parent_id, author_id, text, time):
|
def add_comment_to_comment(self, parent_id, author_id, text, time):
|
||||||
db = self._db()
|
db = self._db()
|
||||||
c = db.cursor()
|
c = db.cursor()
|
||||||
c.execute('''
|
c.execute(
|
||||||
|
"""
|
||||||
insert into comments(thread_id, parent_id, author_id, text, create_time, modify_time)
|
insert into comments(thread_id, parent_id, author_id, text, create_time, modify_time)
|
||||||
select thread_id, ?, ?, ?, ?, ?
|
select thread_id, ?, ?, ?, ?, ?
|
||||||
from comments, users
|
from comments, users
|
||||||
where comment_id = ? and user_id = ? and banned_until < ?
|
where comment_id = ? and user_id = ? and banned_until < ?
|
||||||
''',
|
""",
|
||||||
(parent_id, author_id, text, time, time, parent_id, author_id, time)
|
(parent_id, author_id, text, time, time, parent_id, author_id, time),
|
||||||
)
|
)
|
||||||
if c.rowcount > 0:
|
if c.rowcount > 0:
|
||||||
c.execute('''
|
c.execute(
|
||||||
|
"""
|
||||||
update threads
|
update threads
|
||||||
set update_time = ?
|
set update_time = ?
|
||||||
where threads.thread_id = (
|
where threads.thread_id = (
|
||||||
@@ -353,8 +456,8 @@ class DB:
|
|||||||
from comments c
|
from comments c
|
||||||
where comment_id = ?
|
where comment_id = ?
|
||||||
)
|
)
|
||||||
''',
|
""",
|
||||||
(time, parent_id)
|
(time, parent_id),
|
||||||
)
|
)
|
||||||
db.commit()
|
db.commit()
|
||||||
return True
|
return True
|
||||||
@@ -363,7 +466,8 @@ class DB:
|
|||||||
def modify_thread(self, thread_id, user_id, title, text, time):
|
def modify_thread(self, thread_id, user_id, title, text, time):
|
||||||
db = self._db()
|
db = self._db()
|
||||||
c = db.cursor()
|
c = db.cursor()
|
||||||
c.execute('''
|
c.execute(
|
||||||
|
"""
|
||||||
update threads
|
update threads
|
||||||
set title = ?, text = ?, modify_time = ?
|
set title = ?, text = ?, modify_time = ?
|
||||||
where thread_id = ? and (
|
where thread_id = ? and (
|
||||||
@@ -371,13 +475,17 @@ class DB:
|
|||||||
-- 1 = moderator, 2 = admin
|
-- 1 = moderator, 2 = admin
|
||||||
or (select 1 from users where user_id = ? and (role = 1 or role = 2))
|
or (select 1 from users where user_id = ? and (role = 1 or role = 2))
|
||||||
)
|
)
|
||||||
''',
|
""",
|
||||||
(
|
(
|
||||||
title, text, time,
|
title,
|
||||||
|
text,
|
||||||
|
time,
|
||||||
thread_id,
|
thread_id,
|
||||||
user_id, user_id, time,
|
|
||||||
user_id,
|
user_id,
|
||||||
)
|
user_id,
|
||||||
|
time,
|
||||||
|
user_id,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
if c.rowcount > 0:
|
if c.rowcount > 0:
|
||||||
db.commit()
|
db.commit()
|
||||||
@@ -387,7 +495,8 @@ class DB:
|
|||||||
def modify_comment(self, comment_id, user_id, text, time):
|
def modify_comment(self, comment_id, user_id, text, time):
|
||||||
db = self._db()
|
db = self._db()
|
||||||
c = db.cursor()
|
c = db.cursor()
|
||||||
c.execute('''
|
c.execute(
|
||||||
|
"""
|
||||||
update comments
|
update comments
|
||||||
set text = ?, modify_time = ?
|
set text = ?, modify_time = ?
|
||||||
where comment_id = ? and (
|
where comment_id = ? and (
|
||||||
@@ -395,13 +504,16 @@ class DB:
|
|||||||
-- 1 = moderator, 2 = admin
|
-- 1 = moderator, 2 = admin
|
||||||
or (select 1 from users where user_id = ? and (role = 1 or role = 2))
|
or (select 1 from users where user_id = ? and (role = 1 or role = 2))
|
||||||
)
|
)
|
||||||
''',
|
""",
|
||||||
(
|
(
|
||||||
text, time,
|
text,
|
||||||
|
time,
|
||||||
comment_id,
|
comment_id,
|
||||||
user_id, user_id, time,
|
|
||||||
user_id,
|
user_id,
|
||||||
)
|
user_id,
|
||||||
|
time,
|
||||||
|
user_id,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
if c.rowcount > 0:
|
if c.rowcount > 0:
|
||||||
db.commit()
|
db.commit()
|
||||||
@@ -409,19 +521,20 @@ class DB:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def register_user(self, username, password, time):
|
def register_user(self, username, password, time):
|
||||||
'''
|
"""
|
||||||
Add a user if registrations are enabled.
|
Add a user if registrations are enabled.
|
||||||
'''
|
"""
|
||||||
try:
|
try:
|
||||||
db = self._db()
|
db = self._db()
|
||||||
c = db.cursor()
|
c = db.cursor()
|
||||||
c.execute('''
|
c.execute(
|
||||||
|
"""
|
||||||
insert into users(name, password, join_time)
|
insert into users(name, password, join_time)
|
||||||
select lower(?), ?, ?
|
select lower(?), ?, ?
|
||||||
from config
|
from config
|
||||||
where registration_enabled = 1
|
where registration_enabled = 1
|
||||||
''',
|
""",
|
||||||
(username, password, time)
|
(username, password, time),
|
||||||
)
|
)
|
||||||
if c.rowcount > 0:
|
if c.rowcount > 0:
|
||||||
db.commit()
|
db.commit()
|
||||||
@@ -429,12 +542,13 @@ class DB:
|
|||||||
# up by name.
|
# up by name.
|
||||||
# ROWID is *probably* not always consistent (race conditions).
|
# ROWID is *probably* not always consistent (race conditions).
|
||||||
# Ideally we get the ID immediately on insert.
|
# Ideally we get the ID immediately on insert.
|
||||||
return c.execute('''
|
return c.execute(
|
||||||
|
"""
|
||||||
select user_id
|
select user_id
|
||||||
from users
|
from users
|
||||||
where name = lower(?)
|
where name = lower(?)
|
||||||
''',
|
""",
|
||||||
(username,)
|
(username,),
|
||||||
).fetchone()
|
).fetchone()
|
||||||
return None
|
return None
|
||||||
except sqlite3.IntegrityError:
|
except sqlite3.IntegrityError:
|
||||||
@@ -442,17 +556,18 @@ class DB:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def add_user(self, username, password, time):
|
def add_user(self, username, password, time):
|
||||||
'''
|
"""
|
||||||
Add a user without checking if registrations are enabled.
|
Add a user without checking if registrations are enabled.
|
||||||
'''
|
"""
|
||||||
try:
|
try:
|
||||||
db = self._db()
|
db = self._db()
|
||||||
c = db.cursor()
|
c = db.cursor()
|
||||||
c.execute('''
|
c.execute(
|
||||||
|
"""
|
||||||
insert into users(name, password, join_time)
|
insert into users(name, password, join_time)
|
||||||
values (lower(?), ?, ?)
|
values (lower(?), ?, ?)
|
||||||
''',
|
""",
|
||||||
(username, password, time)
|
(username, password, time),
|
||||||
)
|
)
|
||||||
if c.rowcount > 0:
|
if c.rowcount > 0:
|
||||||
db.commit()
|
db.commit()
|
||||||
@@ -463,90 +578,102 @@ class DB:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def get_users(self):
|
def get_users(self):
|
||||||
return self._db().execute('''
|
return self._db().execute(
|
||||||
|
"""
|
||||||
select user_id, name, join_time, role, banned_until
|
select user_id, name, join_time, role, banned_until
|
||||||
from users
|
from users
|
||||||
''',
|
""",
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_forum_name(self, forum_id, name):
|
def set_forum_name(self, forum_id, name):
|
||||||
return self.change_one('''
|
return self.change_one(
|
||||||
|
"""
|
||||||
update forums
|
update forums
|
||||||
set name = ?
|
set name = ?
|
||||||
where forum_id = ?
|
where forum_id = ?
|
||||||
''',
|
""",
|
||||||
(name, forum_id)
|
(name, forum_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_forum_description(self, forum_id, description):
|
def set_forum_description(self, forum_id, description):
|
||||||
return self.change_one('''
|
return self.change_one(
|
||||||
|
"""
|
||||||
update forums
|
update forums
|
||||||
set description = ?
|
set description = ?
|
||||||
where forum_id = ?
|
where forum_id = ?
|
||||||
''',
|
""",
|
||||||
(description, forum_id)
|
(description, forum_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
def add_forum(self, name, description):
|
def add_forum(self, name, description):
|
||||||
db = self._db()
|
db = self._db()
|
||||||
db.execute('''
|
db.execute(
|
||||||
|
"""
|
||||||
insert into forums(name, description)
|
insert into forums(name, description)
|
||||||
values (?, ?)
|
values (?, ?)
|
||||||
''',
|
""",
|
||||||
(name, description)
|
(name, description),
|
||||||
)
|
)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
def set_config(self, server_name, server_description, registration_enabled):
|
def set_config(
|
||||||
return self.change_one('''
|
self, server_name, server_description, registration_enabled, login_required
|
||||||
|
):
|
||||||
|
return self.change_one(
|
||||||
|
"""
|
||||||
update config
|
update config
|
||||||
set name = ?, description = ?, registration_enabled = ?
|
set name = ?, description = ?, registration_enabled = ?, login_required = ?
|
||||||
''',
|
""",
|
||||||
(server_name, server_description, registration_enabled)
|
(server_name, server_description, registration_enabled, login_required),
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_config_secrets(self, secret_key, captcha_key):
|
def set_config_secrets(self, secret_key, captcha_key):
|
||||||
return self.change_one('''
|
return self.change_one(
|
||||||
|
"""
|
||||||
update config
|
update config
|
||||||
set secret_key = ?, captcha_key = ?
|
set secret_key = ?, captcha_key = ?
|
||||||
''',
|
""",
|
||||||
(secret_key, captcha_key)
|
(secret_key, captcha_key),
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_user_ban(self, user_id, until):
|
def set_user_ban(self, user_id, until):
|
||||||
return self.change_one('''
|
return self.change_one(
|
||||||
|
"""
|
||||||
update users
|
update users
|
||||||
set banned_until = ?
|
set banned_until = ?
|
||||||
where user_id = ?
|
where user_id = ?
|
||||||
''',
|
""",
|
||||||
(until, user_id)
|
(until, user_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_user_role(self, user_id, role):
|
def set_user_role(self, user_id, role):
|
||||||
return self.change_one('''
|
return self.change_one(
|
||||||
|
"""
|
||||||
update users
|
update users
|
||||||
set role = ?
|
set role = ?
|
||||||
where user_id = ?
|
where user_id = ?
|
||||||
''',
|
""",
|
||||||
(role, user_id)
|
(role, user_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_thread_hidden(self, thread_id, hide):
|
def set_thread_hidden(self, thread_id, hide):
|
||||||
return self.change_one('''
|
return self.change_one(
|
||||||
|
"""
|
||||||
update threads
|
update threads
|
||||||
set hidden = ?
|
set hidden = ?
|
||||||
where thread_id = ?
|
where thread_id = ?
|
||||||
''',
|
""",
|
||||||
(hide, thread_id)
|
(hide, thread_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_comment_hidden(self, comment_id, hide):
|
def set_comment_hidden(self, comment_id, hide):
|
||||||
return self.change_one('''
|
return self.change_one(
|
||||||
|
"""
|
||||||
update comments
|
update comments
|
||||||
set hidden = ?
|
set hidden = ?
|
||||||
where comment_id = ?
|
where comment_id = ?
|
||||||
''',
|
""",
|
||||||
(hide, comment_id)
|
(hide, comment_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
def change_one(self, query, values):
|
def change_one(self, query, values):
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ $SQLITE "$1" -init schema.txt "insert into config (
|
|||||||
description,
|
description,
|
||||||
secret_key,
|
secret_key,
|
||||||
captcha_key,
|
captcha_key,
|
||||||
registration_enabled
|
registration_enabled,
|
||||||
|
login_required
|
||||||
)
|
)
|
||||||
values (
|
values (
|
||||||
'agreper-v0.1.1',
|
'agreper-v0.1.1',
|
||||||
@@ -43,7 +44,8 @@ values (
|
|||||||
'',
|
'',
|
||||||
'$(head -c 30 /dev/urandom | base64)',
|
'$(head -c 30 /dev/urandom | base64)',
|
||||||
'$(head -c 30 /dev/urandom | base64)',
|
'$(head -c 30 /dev/urandom | base64)',
|
||||||
0
|
0,
|
||||||
|
0
|
||||||
)"
|
)"
|
||||||
if [ "$2" != --no-admin ]
|
if [ "$2" != --no-admin ]
|
||||||
then
|
then
|
||||||
|
|||||||
50
minimd.py
50
minimd.py
@@ -1,58 +1,70 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
import markdown2
|
||||||
|
|
||||||
# https://stackoverflow.com/a/6041965
|
# https://stackoverflow.com/a/6041965
|
||||||
RE_URL = re.compile(r'(https?://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-]))')
|
RE_URL = re.compile(
|
||||||
RE_EM = re.compile(r'\*(.*?)\*')
|
r"(https?://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-]))"
|
||||||
RE_LIST = re.compile(r'(-|[0-9]\.) .*')
|
)
|
||||||
|
RE_EM = re.compile(r"\*(.*?)\*")
|
||||||
|
RE_LIST = re.compile(r"(-|[0-9]\.) .*")
|
||||||
|
|
||||||
|
RE_PLAINURL = re.compile(
|
||||||
|
r"([ |\n])(https?://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-]))[^\)]"
|
||||||
|
)
|
||||||
|
|
||||||
def html(text):
|
def html(text):
|
||||||
|
text = RE_PLAINURL.sub(r'\1[\2](\2)', text)
|
||||||
|
return markdown2.markdown(text)
|
||||||
|
|
||||||
|
|
||||||
|
def html_old(text):
|
||||||
# Replace angle brackets to prevent XSS
|
# Replace angle brackets to prevent XSS
|
||||||
# Also replace ampersands to prevent surprises.
|
# Also replace ampersands to prevent surprises.
|
||||||
text = text.replace('&', '&').replace('<', '<').replace('>', '>')
|
text = text.replace("&", "&").replace("<", "<").replace(">", ">")
|
||||||
|
|
||||||
html = ['<p>']
|
html = ["<p>"]
|
||||||
lines = text.split('\n')
|
lines = text.split("\n")
|
||||||
in_code = False
|
in_code = False
|
||||||
in_list = False
|
in_list = False
|
||||||
|
|
||||||
for l in lines:
|
for l in lines:
|
||||||
if l == '':
|
if l == "":
|
||||||
in_list = False
|
in_list = False
|
||||||
if in_code:
|
if in_code:
|
||||||
html.append('</pre>')
|
html.append("</pre>")
|
||||||
in_code = False
|
in_code = False
|
||||||
html.append('</p><p>')
|
html.append("</p><p>")
|
||||||
continue
|
continue
|
||||||
if l.startswith(' '):
|
if l.startswith(" "):
|
||||||
in_list = False
|
in_list = False
|
||||||
l = l[2:]
|
l = l[2:]
|
||||||
if not in_code:
|
if not in_code:
|
||||||
html.append('<pre>')
|
html.append("<pre>")
|
||||||
in_code = True
|
in_code = True
|
||||||
html.append(l)
|
html.append(l)
|
||||||
continue
|
continue
|
||||||
if in_code:
|
if in_code:
|
||||||
html.append('</pre>')
|
html.append("</pre>")
|
||||||
in_code = False
|
in_code = False
|
||||||
l = RE_EM.sub(r'<em>\1</em>', l)
|
l = RE_EM.sub(r"<em>\1</em>", l)
|
||||||
l = RE_URL.sub(r'<a href="\1">\1</a>', l)
|
l = RE_URL.sub(r'<a href="\1">\1</a>', l)
|
||||||
if RE_LIST.match(l):
|
if RE_LIST.match(l):
|
||||||
if in_list:
|
if in_list:
|
||||||
html.append('<br>')
|
html.append("<br>")
|
||||||
in_list = True
|
in_list = True
|
||||||
else:
|
else:
|
||||||
in_list = False
|
in_list = False
|
||||||
html.append(l)
|
html.append(l)
|
||||||
|
|
||||||
if in_code:
|
if in_code:
|
||||||
html.append('</pre>')
|
html.append("</pre>")
|
||||||
html.append('</p>')
|
html.append("</p>")
|
||||||
return '\n'.join(html)
|
return "\n".join(html)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
import sys
|
import sys
|
||||||
print(html(sys.stdin.read()))
|
|
||||||
|
|
||||||
|
print(html_old(sys.stdin.read()))
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import passlib.hash
|
import passlib.hash
|
||||||
|
|
||||||
|
|
||||||
def hash(password):
|
def hash(password):
|
||||||
return passlib.hash.argon2.hash(password)
|
return passlib.hash.argon2.hash(password)
|
||||||
|
|
||||||
|
|
||||||
def verify(password, hash):
|
def verify(password, hash):
|
||||||
return passlib.hash.argon2.verify(password, hash)
|
return passlib.hash.argon2.verify(password, hash)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,3 +2,4 @@ argon2-cffi==21.3.0
|
|||||||
Flask==2.2.2
|
Flask==2.2.2
|
||||||
gunicorn==20.1.0
|
gunicorn==20.1.0
|
||||||
passlib==1.7.4
|
passlib==1.7.4
|
||||||
|
markdown2==2.4.9
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ create table config (
|
|||||||
description text not null,
|
description text not null,
|
||||||
secret_key text not null,
|
secret_key text not null,
|
||||||
captcha_key text not null,
|
captcha_key text not null,
|
||||||
registration_enabled boolean not null
|
registration_enabled boolean not null,
|
||||||
|
login_required boolean not null
|
||||||
);
|
);
|
||||||
|
|
||||||
create table users (
|
create table users (
|
||||||
|
|||||||
@@ -21,6 +21,10 @@
|
|||||||
<td>Registration enabled</td>
|
<td>Registration enabled</td>
|
||||||
<td><input name=registration_enabled type=checkbox {{ 'checked' if config.registration_enabled else '' }}></td>
|
<td><input name=registration_enabled type=checkbox {{ 'checked' if config.registration_enabled else '' }}></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Login required</td>
|
||||||
|
<td><input name=login_required type=checkbox {{ 'checked' if config.login_required else '' }}></td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<input type=submit value=Update>
|
<input type=submit value=Update>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
{% from 'comment.html' import render_comment, render_comment_pre, render_comment_post, reply with context %}
|
{% from 'comment.html' import render_comment, render_comment_pre, render_comment_post, reply with context %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<p><span> « </span><a href="{{ url_for('forum', forum_id = forum_id) }}">{{ forum_title }}</a><span> « </span><a href="{{ url_for('thread', thread_id = thread_id) }}">{{ title }}</a></p>
|
||||||
{{ render_comment_pre(reply_comment, thread_id, comments | length == 0) }}
|
{{ render_comment_pre(reply_comment, thread_id, comments | length == 0) }}
|
||||||
|
|
||||||
{{ reply() }}
|
{{ reply() }}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
{% block content -%}
|
{% block content -%}
|
||||||
<p>{{ minimd(description) | safe }}</p>
|
<p>{{ minimd(description) | safe }}</p>
|
||||||
<p><a href="{{ url_for('new_thread', forum_id = forum_id) }}">Create thread</a></p>
|
<p><span> « </span><a href="{{ url_for('index') }}">Forum list</a><span> | </span><a href="{{ url_for('new_thread', forum_id = forum_id) }}">Create thread</a></p>
|
||||||
{{- nav() -}}
|
{{- nav() -}}
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
{%- from 'moderator.html' import moderate_thread with context %}
|
{%- from 'moderator.html' import moderate_thread with context %}
|
||||||
|
|
||||||
{%- block content %}
|
{%- block content %}
|
||||||
|
<p><span> « </span><a href="{{ url_for('forum', forum_id = forum_id) }}">{{ forum_title }}</a></p>
|
||||||
{%- if user is not none and user.is_moderator() -%}
|
{%- if user is not none and user.is_moderator() -%}
|
||||||
<p>{{ moderate_thread(thread_id, hidden) }}</p>
|
<p>{{ moderate_thread(thread_id, hidden) }}</p>
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
|
|||||||
13
tool.py
13
tool.py
@@ -2,24 +2,27 @@
|
|||||||
|
|
||||||
import sys, password
|
import sys, password
|
||||||
|
|
||||||
|
|
||||||
def arg(i, s):
|
def arg(i, s):
|
||||||
if i < len(sys.argv):
|
if i < len(sys.argv):
|
||||||
return sys.argv[i]
|
return sys.argv[i]
|
||||||
print(s)
|
print(s)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def arg_last(i, s):
|
def arg_last(i, s):
|
||||||
if i == len(sys.argv) - 1:
|
if i == len(sys.argv) - 1:
|
||||||
return sys.argv[i]
|
return sys.argv[i]
|
||||||
print(s)
|
print(s)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
proc = 'tool.py' if len(sys.argv) < 1 else sys.argv[0]
|
|
||||||
cmd = arg(1, f'usage: {proc} <command> [...]')
|
|
||||||
|
|
||||||
if cmd == 'password':
|
proc = "tool.py" if len(sys.argv) < 1 else sys.argv[0]
|
||||||
pwd = arg_last(2, 'usage: {proc} password <pwd>')
|
cmd = arg(1, f"usage: {proc} <command> [...]")
|
||||||
|
|
||||||
|
if cmd == "password":
|
||||||
|
pwd = arg_last(2, "usage: {proc} password <pwd>")
|
||||||
print(password.hash(pwd))
|
print(password.hash(pwd))
|
||||||
else:
|
else:
|
||||||
print('unknown command ', cmd)
|
print("unknown command ", cmd)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|||||||
Reference in New Issue
Block a user