Convers all text files to LF instead of CRLF.

This commit is contained in:
Circling Skies 2024-12-11 23:35:41 -03:00
parent 6e02e322a6
commit f2caae393d
15 changed files with 1572 additions and 1799 deletions

View file

@ -6,6 +6,3 @@ loop: loop.asd loop.lisp scripts/build-exe.lisp
install: loop
mkdir -p `head -1 conf-home` && \
cp -R loop accounts.lisp groups scripts `head -1 conf-home`
clean:
rm -f *.pdf *.out *.aux *.log *.fls *.fdb_latexmk loop loop.tex

306
README
View file

@ -1,153 +1,153 @@
(*) Introduction
LOOP is an NNTP server written in Common Lisp.
(*) Assumptions
We assume
- you run SBCL
- you have Quicklisp installed and knows how to use it
- you know how to use a TCP server such as
https://cr.yp.to/ucspi-tcp.html
- you know how to manage a daemon with
https://cr.yp.to/daemontools.html
- you have git and knows how to use it
(*) How to install it
LOOP is not in the Quicklisp repository, so we'll instruct you to
install it as a local project. Go to
~/quicklisp/local-projects/
and say
$ git clone https://git.antartida.xyz/loop/srv loop
$ cd loop
$ make
If you just installed SBCL and quicklisp, the build might take a
little while to download dependencies. Be patient. Now you have the
executable loop. Try it out:
--8<-------------------------------------------------------->8---
$ ./loop --help
NAME:
loop - An NNTP server for a circle of friends.
USAGE:
loop [options] [arguments ...]
OPTIONS:
--change-passwd <VALUE> <username> <new-password> changes password
--create-account <VALUE> <username> <invited-by> creates a new account
--help display usage information and exit
--logging turn on debug logging on stderr
--version display version and exit
-l, --list-accounts lists accounts
-r, --repl run a REPL on port 4006
-s, --server runs NNTP server reading from stdout
AUTHORS:
Circling Skies <loop@antartida.xyz>
LICENSE:
GPL v3
--8<-------------------------------------------------------->8---
You can talk to the NNTP server with -s:
--8<-------------------------------------------------------->8---
$ ./loop -s
200 Welcome! Say ``help'' for a menu.
quit
205 Good-bye.
--8<-------------------------------------------------------->8---
It's time to choose a directory from which LOOP will run. Say you
choose $HOME/loop. Then say
$ echo $HOME/loop > conf-home
$ make install
Whenever you run loop, make sure you're in its home directory because
it will look for the file accounts.lisp always relatively to the
current working directory of the process. (So, if you set up a cron
job, make sure the job, too, sets LOOP's home directory as its current
working directory.)
(*) Create your account
LOOP requires authentication for most things, so you should create an
account for you right away. Accounts are kept in accounts.lisp in
your installation directory. Every time you create an account, you
must specify who is inviting this new account into the LOOP---because
we keep a tree of accounts. The root account is called anonymous, so
your first account must be invited by the anonymous account. So you
can say
./loop --create-account you anonymous
The anonymous account has no special power. It exists solely because
the graph of accounts needs a root.
(*) How to expose it to the network
Just have your system run your TCP server of choice. For instance, if
you're using djb's tcpserver and would like LOOP to listen on port
1024, tell your shell
--8<-------------------------------------------------------->8---
$ tcpserver -v -HR 0.0.0.0 1024 ./loop -s
tcpserver: status: 0/40
--8<-------------------------------------------------------->8---
Using another terminal, telnet to your host on port 1024:
--8<-------------------------------------------------------->8---
$ telnet localhost 1024
Trying 127.0.0.1...
Connected to antartida.xyz.
Escape character is '^]'.
200 Welcome! Say ``help'' for a menu.
quit
205 Good-bye.
Connection closed by foreign host.
--8<-------------------------------------------------------->8---
Directories daemon/ and daemon-tls/ in LOOP's source code have sample
scripts to use with djb's tcpserver and daemontools. If you have
never done this, it will be better education if you learn to use
daemontools and ucspi-tcp before going live with a LOOP community.
It's easy and fun.
(*) Systems with no installation issues
We installed LOOP just fine on
FreeBSD 14.1, 14.2 with SBCL 2.4.9.
Debian GNU/Linux 8.11 codename jessie with SBCL 1.2.4.debian.
(*) Systems with installation issues
We installed LOOP on Ubuntu 24.04 (24.01.1 LTS) codename noble with
SBCL 2.2.9.debian. We found that CLSQL could not load the shared
object libsqlite3.so because ``apt install libsqlite3'' installs the
library at
/usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
with a symbolic link to libsqlite3.so.0, but not to libsqlite3.so.
SBCL is trying to load libsqlite3.so, so a solution is to just tell
your system to
ln -s libsqlite3.so.0 libsqlite3.so
at /usr/lib/x86_64-linux-gnu.
(*) Introduction
LOOP is an NNTP server written in Common Lisp.
(*) Assumptions
We assume
- you run SBCL
- you have Quicklisp installed and knows how to use it
- you know how to use a TCP server such as
https://cr.yp.to/ucspi-tcp.html
- you know how to manage a daemon with
https://cr.yp.to/daemontools.html
- you have git and knows how to use it
(*) How to install it
LOOP is not in the Quicklisp repository, so we'll instruct you to
install it as a local project. Go to
~/quicklisp/local-projects/
and say
$ git clone https://git.antartida.xyz/loop/srv loop
$ cd loop
$ make
If you just installed SBCL and quicklisp, the build might take a
little while to download dependencies. Be patient. Now you have the
executable loop. Try it out:
--8<-------------------------------------------------------->8---
$ ./loop --help
NAME:
loop - An NNTP server for a circle of friends.
USAGE:
loop [options] [arguments ...]
OPTIONS:
--change-passwd <VALUE> <username> <new-password> changes password
--create-account <VALUE> <username> <invited-by> creates a new account
--help display usage information and exit
--logging turn on debug logging on stderr
--version display version and exit
-l, --list-accounts lists accounts
-r, --repl run a REPL on port 4006
-s, --server runs NNTP server reading from stdout
AUTHORS:
Circling Skies <loop@antartida.xyz>
LICENSE:
GPL v3
--8<-------------------------------------------------------->8---
You can talk to the NNTP server with -s:
--8<-------------------------------------------------------->8---
$ ./loop -s
200 Welcome! Say ``help'' for a menu.
quit
205 Good-bye.
--8<-------------------------------------------------------->8---
It's time to choose a directory from which LOOP will run. Say you
choose $HOME/loop. Then say
$ echo $HOME/loop > conf-home
$ make install
It's time to create an account you. Whenever you run loop, make sure
you're in its home directory because it will look for the file
accounts.lisp always relatively to the current working directory of
the process. (So, if you set up a cron job later on, make sure the
job, too, sets LOOP's home directory as its current working
directory.)
(*) Create your account
LOOP requires authentication for most things, so you should create an
account for you right away. Accounts are kept in accounts.lisp in
your installation directory. Every time you create an account, you
must specify who is inviting this new account into the LOOP---because
we keep a tree of accounts. The root account is called anonymous, so
your first account must be invited by the anonymous account. So you
can say
./loop --create-account you anonymous
The anonymous account has no special power. It exists solely because
the graph of accounts needs a root.
(*) How to expose it to the network
Run your TCP server of choice. For instance, if you're using djb's
tcpserver and would like LOOP to listen on port 1024, tell your shell
--8<-------------------------------------------------------->8---
$ tcpserver -v -HR 0.0.0.0 1024 ./loop -s
tcpserver: status: 0/40
--8<-------------------------------------------------------->8---
Using another terminal, telnet to your host on port 1024:
--8<-------------------------------------------------------->8---
$ telnet localhost 1024
Trying 127.0.0.1...
Connected to antartida.xyz.
Escape character is '^]'.
200 Welcome! Say ``help'' for a menu.
quit
205 Good-bye.
Connection closed by foreign host.
--8<-------------------------------------------------------->8---
Directories daemon/ and daemon-tls/ in LOOP's source code have sample
scripts to use with djb's tcpserver and daemontools. If you have
never done this, it will be better education if you learn to use
daemontools and ucspi-tcp before going live with a LOOP community.
It's easy and fun.
(*) Systems with no installation issues
We installed LOOP just fine on
FreeBSD 14.1, 14.2 with SBCL 2.4.9.
Debian GNU/Linux 8.11 codename jessie with SBCL 1.2.4.debian.
(*) Systems with installation issues
We installed LOOP on Ubuntu 24.04 (24.01.1 LTS) codename noble with
SBCL 2.2.9.debian. We found that CLSQL could not load the shared
object libsqlite3.so because ``apt install libsqlite3'' installs the
library at
/usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
with a symbolic link to libsqlite3.so.0, but not to libsqlite3.so.
SBCL is trying to load libsqlite3.so, so a solution is to just tell
your system to
ln -s libsqlite3.so.0 libsqlite3.so
at /usr/lib/x86_64-linux-gnu.

View file

@ -1,4 +1,4 @@
In this service example, we're using tlswrapper by Jan Mojžíš, the
source of which you can find at
https://github.com/janmojzis/tlswrapper
In this service example, we're using tlswrapper by Jan Mojžíš, the
source of which you can find at
https://github.com/janmojzis/tlswrapper

View file

@ -1,3 +1,3 @@
#!/bin/sh
echo loop-tls-logger
exec /usr/bin/logger -i -t loop-tls
#!/bin/sh
echo loop-tls-logger
exec /usr/bin/logger -i -t loop-tls

View file

@ -1,7 +1,7 @@
#!/bin/sh
echo loop-tls
cd /path/to/loop
exec tcpserver -HR 0.0.0.0 563 \
/usr/bin/tlswrapper -f \
/usr/local/etc/letsencrypt/live/mydomain/cert-priv1.pem \
/path/to/loop
#!/bin/sh
echo loop-tls
cd /path/to/loop
exec tcpserver -HR 0.0.0.0 563 \
/usr/bin/tlswrapper -f \
/usr/local/etc/letsencrypt/live/mydomain/cert-priv1.pem \
/path/to/loop

View file

@ -1,7 +1,7 @@
Date: 2024-03-07 21:44:31 GMT-3
Message-Id: <edjocyeqzqqhnswlbrbo@loop>
From: Loop
Subject: let there be light
Newsgroups: local.control.news
Administrative news will be posted here by me. -- Loop
Date: 2024-03-07 21:44:31 GMT-3
Message-Id: <edjocyeqzqqhnswlbrbo@loop>
From: Loop
Subject: let there be light
Newsgroups: local.control.news
Administrative news will be posted here by me. -- Loop

View file

@ -1,7 +1,7 @@
Date: 2024-03-07 21:44:31 GMT-3
Message-Id: <edjocyeqzqqhnswlbrbo@loop>
From: Loop
Subject: let there be light
Newsgroups: local.test
A sample group.
Date: 2024-03-07 21:44:31 GMT-3
Message-Id: <edjocyeqzqqhnswlbrbo@loop>
From: Loop
Subject: let there be light
Newsgroups: local.test
A sample group.

View file

@ -1,7 +1,7 @@
;;; -*- Mode: LISP; Syntax: COMMON-LISP; -*-
(asdf:defsystem :loop
:version "0.1"
:description "An NNTP server for a circle of friends."
:depends-on (:lisp-unit :str :uiop :cl-ppcre :local-time :clsql-sqlite3 :clingon
:filesystem-utils)
:components ((:file "loop")))
;;; -*- Mode: LISP; Syntax: COMMON-LISP; -*-
(asdf:defsystem :loop
:version "0.1"
:description "An NNTP server for a circle of friends."
:depends-on (:lisp-unit :str :uiop :cl-ppcre :local-time :clsql-sqlite3 :clingon
:filesystem-utils)
:components ((:file "loop")))

2660
loop.lisp

File diff suppressed because it is too large Load diff

26
loop.nw
View file

@ -260,19 +260,19 @@ the response. You can get a string version of these numbers:
\begin{verbatim}
LOOP> (bytes->string (response-data (dispatch (make-request :verb "HELP"))))
"What's on the menu today?
ARTICLE fetches full articles
AUTHINFO makes me trust you
BODY fetches an article body
GROUP sets the current group
HEAD fetches article headers
HELP displays this menu
LIST lists all groups
MODE handles the mode request from clients
NEXT increments the article pointer
POST posts your article
QUIT politely says good-bye
XDD displays your state of affairs
"What's on the menu today?
ARTICLE fetches full articles
AUTHINFO makes me trust you
BODY fetches an article body
GROUP sets the current group
HEAD fetches article headers
HELP displays this menu
LIST lists all groups
MODE handles the mode request from clients
NEXT increments the article pointer
POST posts your article
QUIT politely says good-bye
XDD displays your state of affairs
XOVER fetches the overview database of a group"
\end{verbatim}

224
peat
View file

@ -1,224 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf8 -*-
##############################
# ____ ___ ____ ______ #
# | \ / _] / T| T #
# | o )/ [_ Y o || | #
# | _/Y _]| |l_j l_j #
# | | | [_ | _ | | | #
# | | | T| | | | | #
# l__j l_____jl__j__j l__j #
# #
##### #####
# Repeat commands! #
##################
import errno, os, subprocess, sys, time
from optparse import OptionParser
interval = 1.0
command = 'true'
clear = True
get_paths = lambda: set()
verbose = True
dynamic = None
last_run = None
USAGE = r"""usage: %prog [options] COMMAND
COMMAND should be given as a single argument using a shell string.
A list of paths to watch should be piped in on standard input.
For example:
find . | peat './test.sh'
find . -name '*.py' | peat 'rm *.pyc'
find . -name '*.py' -print0 | peat -0 'rm *.pyc'
If --dynamic is used, the given command will be run each time to generate the
list of files to check:
peat --dynamic 'find .' './test.sh'
peat --dynamic 'find . -name '\''*.py'\''' 'rm *.pyc'
"""
def log(s):
if verbose:
print(s)
def die(s):
sys.stderr.write('ERROR: ' + s + '\n')
sys.exit(1)
def check(paths):
for p in paths:
try:
if os.stat(p).st_mtime >= last_run:
return True
except OSError as e:
# If the file has been deleted since we started watching, don't
# worry about it.
if e.errno == errno.ENOENT:
pass
else:
raise
return False
def run():
global last_run
last_run = time.time()
log("running: " + command)
subprocess.call(command, shell=True)
def build_option_parser():
p = OptionParser(USAGE)
# Main options
p.add_option('-i', '--interval', default=None,
help='interval between checks in milliseconds',
metavar='N')
p.add_option('-I', '--smart-interval', dest='interval',
action='store_const', const=None,
help='determine the interval based on number of files watched (default)')
p.add_option('-d', '--dynamic', default=None,
help='run COMMAND before each run to generate the list of files to check',
metavar='COMMAND')
p.add_option('-D', '--no-dynamic', dest='dynamic',
action='store_const', const=None,
help='take a list of files to watch on standard in (default)')
p.add_option('-c', '--clear', default=True,
action='store_true', dest='clear',
help='clear screen before runs (default)')
p.add_option('-C', '--no-clear',
action='store_false', dest='clear',
help="don't clear screen before runs")
p.add_option('-v', '--verbose', default=True,
action='store_true', dest='verbose',
help='show extra logging output (default)')
p.add_option('-q', '--quiet',
action='store_false', dest='verbose',
help="don't show extra logging output")
p.add_option('-w', '--whitespace', default=None,
action='store_const', dest='sep', const=None,
help="assume paths are separated by whitespace (default)")
p.add_option('-n', '--newlines',
action='store_const', dest='sep', const='\n',
help="assume paths are separated by newlines")
p.add_option('-s', '--spaces',
action='store_const', dest='sep', const=' ',
help="assume paths are separated by spaces")
p.add_option('-0', '--zero',
action='store_const', dest='sep', const='\0',
help="assume paths are separated by null bytes")
return p
def _main():
if dynamic:
log("Running the following command to generate watch list:")
log(' ' + dynamic)
log('')
log("Watching the following paths:")
for p in get_paths():
log(' ' + p)
log('')
log('Checking for changes every %d milliseconds.' % int(interval * 1000))
log('')
run()
while True:
time.sleep(interval)
if check(get_paths()):
if clear:
subprocess.check_call('clear')
run()
def smart_interval(count):
"""Return the smart interval to use in milliseconds."""
if count >= 50:
return 1000
else:
sq = lambda n: n * n
return int(1000 * (1 - (sq(50.0 - count) / sq(50))))
def _parse_interval(options):
global get_paths
if options.interval:
i = int(options.interval)
elif options.dynamic:
i = 1000
else:
i = smart_interval(len(get_paths()))
return i / 1000.0
def _parse_paths(sep, data):
if not sep:
paths = data.split()
else:
paths = data.split(sep)
paths = [p.rstrip('\n') for p in paths if p]
paths = map(os.path.abspath, paths)
paths = set(paths)
return paths
def main():
global interval, command, clear, get_paths, verbose, dynamic
(options, args) = build_option_parser().parse_args()
if len(args) != 1:
die("exactly one command must be given")
command = args[0]
clear = options.clear
verbose = options.verbose
sep = options.sep
dynamic = options.dynamic
if dynamic:
def _get_paths():
data = subprocess.check_output(dynamic, shell=True)
return _parse_paths(sep, data)
get_paths = _get_paths
else:
data = sys.stdin.read()
paths = _parse_paths(sep, data)
if not paths:
die("no paths to watch were given on standard input")
for path in paths:
if not os.path.exists(path):
die('path to watch does not exist: ' + repr(path))
get_paths = lambda: paths
interval = _parse_interval(options)
_main()
if __name__ == '__main__':
import signal
def sigint_handler(signal, frame):
sys.stdout.write('\n')
sys.exit(130)
signal.signal(signal.SIGINT, sigint_handler)
main()

View file

@ -1,10 +1,10 @@
#-quicklisp
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
(user-homedir-pathname))))
(when (probe-file quicklisp-init)
(load quicklisp-init)))
(ql:quickload :loop :silent t)
(sb-ext:save-lisp-and-die #P"loop"
:toplevel #'loop:main
:executable t
:save-runtime-options t)
#-quicklisp
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
(user-homedir-pathname))))
(when (probe-file quicklisp-init)
(load quicklisp-init)))
(ql:quickload :loop :silent t)
(sb-ext:save-lisp-and-die #P"loop"
:toplevel #'loop:main
:executable t
:save-runtime-options t)

View file

@ -1,11 +1,11 @@
#-quicklisp
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
(user-homedir-pathname))))
(when (probe-file quicklisp-init)
(load quicklisp-init)))
(ql:quickload :loop :silent t)
(in-package #:loop)
(connect-index! "message-id.db")
(drop-create-index!)
(index-from-fs!)
(format t "Index built.~%")
#-quicklisp
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
(user-homedir-pathname))))
(when (probe-file quicklisp-init)
(load quicklisp-init)))
(ql:quickload :loop :silent t)
(in-package #:loop)
(connect-index! "message-id.db")
(drop-create-index!)
(index-from-fs!)
(format t "Index built.~%")

View file

@ -1,11 +1,11 @@
#-quicklisp
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
(user-homedir-pathname))))
(when (probe-file quicklisp-init)
(load quicklisp-init)))
(ql:quickload :loop :silent t)
(in-package #:loop)
(read-accounts!)
(connect-index! "message-id.db")
(remove-inactive-users!)
(write-accounts!)
#-quicklisp
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
(user-homedir-pathname))))
(when (probe-file quicklisp-init)
(load quicklisp-init)))
(ql:quickload :loop :silent t)
(in-package #:loop)
(read-accounts!)
(connect-index! "message-id.db")
(remove-inactive-users!)
(write-accounts!)

View file

@ -1,9 +1,9 @@
#-quicklisp
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
(user-homedir-pathname))))
(when (probe-file quicklisp-init)
(load quicklisp-init)))
(ql:quickload :loop :silent t)
(in-package #:loop)
(migrate-add-creation-and-post-date!)
(format t "Accounts rewritten.~%")
#-quicklisp
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
(user-homedir-pathname))))
(when (probe-file quicklisp-init)
(load quicklisp-init)))
(ql:quickload :loop :silent t)
(in-package #:loop)
(migrate-add-creation-and-post-date!)
(format t "Accounts rewritten.~%")