Compare commits

...
Sign in to create a new pull request.

144 commits

Author SHA1 Message Date
e5cc24064d Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 15s
2025-01-29 22:00:07 -08:00
1c06dff8d0 Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 28s
2025-01-29 21:55:57 -08:00
4d9625d1c9 Update modules/cfd1.py
All checks were successful
/ check (push) Successful in 11s
2025-01-29 21:08:04 -08:00
5fa7ebc06b Update modules/sqlite_local.py
All checks were successful
/ check (push) Successful in 14s
2025-01-29 20:59:13 -08:00
9b3a6bd083 Update modules/sqlite_local.py
All checks were successful
/ check (push) Successful in 14s
2025-01-29 20:51:09 -08:00
c345c7bb9a Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 12s
2025-01-29 20:20:08 -08:00
6bdb2cb127 Update server.py
All checks were successful
/ check (push) Successful in 12s
2025-01-29 20:18:25 -08:00
4ac5292d2c Update server.py
All checks were successful
/ check (push) Successful in 26s
2025-01-29 20:17:03 -08:00
c9e44686b3 Update server.py
All checks were successful
/ check (push) Successful in 10s
2025-01-29 20:12:13 -08:00
ad7a309c92 Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 21s
2025-01-29 20:09:17 -08:00
02e020ff76 Update server.py
All checks were successful
/ check (push) Successful in 12s
2025-01-29 20:04:17 -08:00
2ed7ec5de1 Update server.py
All checks were successful
/ check (push) Successful in 10s
2025-01-29 20:01:49 -08:00
30f923aac8 Update server.py
All checks were successful
/ check (push) Successful in 9s
2025-01-29 19:59:46 -08:00
aaff5b977e Update server.py
Some checks failed
/ check (push) Failing after 14s
2025-01-29 19:57:22 -08:00
50282f850e checkpoitn
Some checks failed
/ check (push) Failing after 11s
2025-01-29 19:28:02 -08:00
c0f2b3b91c Update server.py
All checks were successful
/ check (push) Successful in 24s
2025-01-29 19:12:16 -08:00
b53a82fd4c Update server.py
All checks were successful
/ check (push) Successful in 22s
2025-01-29 19:06:46 -08:00
a444713171 Update server.py
All checks were successful
/ check (push) Successful in 9s
2025-01-28 21:27:34 -08:00
8bfd72c416 Update server.py
All checks were successful
/ check (push) Successful in 14s
2025-01-28 21:26:06 -08:00
6c89da16af Update server.py
All checks were successful
/ check (push) Successful in 10s
2025-01-28 21:22:54 -08:00
b4632c035e Update config.yml
All checks were successful
/ check (push) Successful in 14s
2025-01-28 21:18:06 -08:00
730eec8bc6 Update server.py
All checks were successful
/ check (push) Successful in 11s
2025-01-28 19:06:41 -08:00
9ff0311948 Update server.py
Some checks failed
/ check (push) Failing after 9s
2025-01-28 18:09:15 -08:00
2f33a0199c Update server.py
All checks were successful
/ check (push) Successful in 12s
2025-01-28 18:07:17 -08:00
3ee5f32f5f Update server.py
All checks were successful
/ check (push) Successful in 12s
2025-01-28 14:52:18 -08:00
26db6dc6c7 Update todo.md
All checks were successful
/ check (push) Successful in 9s
2025-01-27 18:42:44 -08:00
d59de1983e Update todo.md
All checks were successful
/ check (push) Successful in 17s
2025-01-27 18:42:20 -08:00
5f8f6512fe Update modules/botnet_protect.py
All checks were successful
/ check (push) Successful in 15s
2025-01-26 20:32:22 -08:00
70acb4f6a6 Update modules/botnet_protect.py
Some checks failed
/ check (push) Failing after 13s
2025-01-26 20:31:02 -08:00
48827c622c Update modules/botnet_protect.py
Some checks failed
/ check (push) Failing after 14s
2025-01-26 20:30:20 -08:00
1e59932237 Update modules/botnet_protect.py
Some checks failed
/ check (push) Failing after 15s
2025-01-26 20:27:35 -08:00
46a0f4e54f Update modules/botnet_protect.py
All checks were successful
/ check (push) Successful in 15s
2025-01-26 20:24:58 -08:00
cf793a99e3 Update modules/botnet_protect.py
All checks were successful
/ check (push) Successful in 19s
2025-01-26 20:24:33 -08:00
418fdf8daa Update server.py
Some checks failed
/ check (push) Failing after 12s
2025-01-26 13:39:12 -08:00
0e5d974900 Update server.py
All checks were successful
/ check (push) Successful in 23s
2025-01-26 13:21:02 -08:00
7d211f7cf7 Update server.py
Some checks failed
/ check (push) Failing after 14s
2025-01-26 11:05:52 -08:00
fd4dcae2c8 Update todo.md
All checks were successful
/ check (push) Successful in 24s
2025-01-26 01:16:16 -08:00
d89f5ff354 Update server.py
Some checks failed
/ check (push) Failing after 42s
2025-01-26 01:08:44 -08:00
6de09ecffd Update server.py
All checks were successful
/ check (push) Successful in 11s
2025-01-26 01:06:47 -08:00
ae23a08881 Update server.py
Some checks failed
/ check (push) Failing after 18s
2025-01-26 01:00:28 -08:00
d6e507ad7f Update server.py
All checks were successful
/ check (push) Successful in 29s
2025-01-26 00:46:02 -08:00
46b6b0daf2 Update server.py
All checks were successful
/ check (push) Successful in 14s
2025-01-26 00:34:16 -08:00
25a6af8af9 Update server.py 2025-01-26 00:32:42 -08:00
3fbc0bfa0b Update server.py
All checks were successful
/ check (push) Successful in 12s
2025-01-25 22:46:41 -08:00
eab9300695 Update server.py
All checks were successful
/ check (push) Successful in 16s
2025-01-25 22:44:24 -08:00
8737f98d5a Update server.py
All checks were successful
/ check (push) Successful in 10s
2025-01-25 22:37:57 -08:00
84927c13ee Update server.py
All checks were successful
/ check (push) Successful in 11s
2025-01-25 22:36:34 -08:00
a2fe2289a3 Update server.py
All checks were successful
/ check (push) Successful in 10s
2025-01-25 22:34:47 -08:00
a91e98b9c2 Update server.py
All checks were successful
/ check (push) Successful in 11s
2025-01-25 22:31:44 -08:00
684554bb4f Update server.py
All checks were successful
/ check (push) Successful in 13s
2025-01-25 22:29:11 -08:00
f30308351f Update server.py
All checks were successful
/ check (push) Successful in 11s
2025-01-25 22:27:32 -08:00
74cb4168de Update server.py
All checks were successful
/ check (push) Successful in 24s
2025-01-25 22:23:10 -08:00
d29688421c Update server.py
All checks were successful
/ check (push) Successful in 10s
2025-01-25 21:38:13 -08:00
8f2d6e70d3 Update server.py
Some checks failed
/ check (push) Failing after 36s
2025-01-25 21:35:21 -08:00
e2ea87ac63 Update server.py
All checks were successful
/ check (push) Successful in 11s
2025-01-25 21:33:22 -08:00
cbf3e35d0e Update server.py
All checks were successful
/ check (push) Successful in 11s
2025-01-25 21:26:53 -08:00
8341deeb20 Update server.py
All checks were successful
/ check (push) Successful in 11s
2025-01-25 21:25:36 -08:00
eceb5804a5 Update server.py
All checks were successful
/ check (push) Successful in 12s
2025-01-25 21:24:53 -08:00
7fb2a000a7 Update server.py
All checks were successful
/ check (push) Successful in 10s
2025-01-25 21:23:24 -08:00
2643d14c63 Update server.py
All checks were successful
/ check (push) Successful in 11s
2025-01-25 21:21:51 -08:00
acdea1a045 Update server.py
All checks were successful
/ check (push) Successful in 25s
2025-01-25 21:20:27 -08:00
06fbc24444 Update server.py
Some checks failed
/ check (push) Failing after 10s
2025-01-25 21:18:44 -08:00
631253a049 Update todo.md
Some checks failed
/ check (push) Failing after 12s
2025-01-25 21:17:42 -08:00
047d2732fc Update server.py
Some checks failed
/ check (push) Failing after 10s
2025-01-25 21:15:19 -08:00
4a95db64d0 Update server.py
All checks were successful
/ check (push) Successful in 12s
2025-01-25 20:36:41 -08:00
4a3ee0e6f0 Update modules/cfd1.py
All checks were successful
/ check (push) Successful in 11s
2025-01-25 19:05:23 -08:00
9aa8095734 Update modules/cfd1.py
All checks were successful
/ check (push) Successful in 10s
2025-01-25 18:47:10 -08:00
375aae29d1 Update modules/cfd1.py
All checks were successful
/ check (push) Successful in 10s
2025-01-25 18:46:45 -08:00
1c619acfe1 Update modules/cfd1.py
All checks were successful
/ check (push) Successful in 16s
2025-01-25 18:46:24 -08:00
69887c4a02 Update server.py
All checks were successful
/ check (push) Successful in 10s
2025-01-25 18:43:43 -08:00
43b6539475 Update requirements.txt
Some checks failed
/ check (push) Has been cancelled
2025-01-25 18:43:36 -08:00
2bd4989fd2 Update modules/cfd1.py
All checks were successful
/ check (push) Successful in 21s
2025-01-25 18:39:41 -08:00
092e328db0 Update modules/cfd1.py
All checks were successful
/ check (push) Successful in 15s
2025-01-25 18:02:25 -08:00
0555acb795 Update .forgejo/workflows/check-syntax.yml
All checks were successful
/ check (push) Successful in 10s
2025-01-24 22:41:51 -08:00
6077c270fb Update server.py
All checks were successful
/ check (push) Successful in 14s
2025-01-24 19:20:23 -08:00
ee962b1a4a Update server.py
All checks were successful
/ check (push) Successful in 17s
2025-01-24 19:18:51 -08:00
5e0a56ebef Update server.py
All checks were successful
/ check (push) Successful in 15s
2025-01-24 19:16:08 -08:00
fbec969db9 Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 25s
2025-01-24 18:33:45 -08:00
5a088bc572 Update server.py
All checks were successful
/ check (push) Successful in 15s
2025-01-23 17:25:11 -08:00
4861080f6b Update config.yml
All checks were successful
/ check (push) Successful in 21s
2025-01-23 11:52:01 -08:00
4ccba31884 Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 15s
2025-01-23 11:32:10 -08:00
c38124ad2f Update server.py
All checks were successful
/ check (push) Successful in 22s
2025-01-23 11:30:39 -08:00
999abf3ef8 Update server.py
All checks were successful
/ check (push) Successful in 20s
2025-01-23 07:53:08 -08:00
087ff7f337 Update server.py
All checks were successful
/ check (push) Successful in 9s
2025-01-22 23:03:37 -08:00
821c91e491 Update server.py
All checks were successful
/ check (push) Successful in 9s
2025-01-22 22:30:48 -08:00
f75d1fcf97 Update todo.md
All checks were successful
/ check (push) Successful in 11s
2025-01-22 22:29:37 -08:00
444d60bb38 Update server.py
All checks were successful
/ check (push) Successful in 10s
2025-01-22 21:11:56 -08:00
f70ede55ca Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 8s
2025-01-22 21:09:09 -08:00
9dad9ea04d Update modules/sqlite_local.py
All checks were successful
/ check (push) Successful in 12s
2025-01-22 20:23:57 -08:00
2c43516dc4 Update modules/sqlite_local.py
All checks were successful
/ check (push) Successful in 12s
2025-01-22 20:21:06 -08:00
f431a7987a Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 14s
2025-01-22 20:19:22 -08:00
9b83b98e58 Update server.py
All checks were successful
/ check (push) Successful in 10s
2025-01-22 19:58:13 -08:00
5d8ad26405 Update server.py
Some checks failed
/ check (push) Failing after 10s
2025-01-22 19:57:41 -08:00
fbe72caf97 Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 19s
2025-01-22 19:35:21 -08:00
302df7eab9 Update modules/sqlite_local.py
All checks were successful
/ check (push) Successful in 24s
2025-01-22 19:13:36 -08:00
dafa4827d5 Update server.py
All checks were successful
/ check (push) Successful in 13s
2025-01-22 16:20:30 -08:00
605a702fb0 Update todo.md
All checks were successful
/ check (push) Successful in 32s
2025-01-22 11:10:03 -08:00
761668535c Update todo.md
All checks were successful
/ check (push) Successful in 9s
2025-01-22 11:06:56 -08:00
af5a39b315 Update todo.md
All checks were successful
/ check (push) Successful in 10s
2025-01-22 11:05:20 -08:00
bccdc881e8 Update todo.md
All checks were successful
/ check (push) Successful in 19s
2025-01-22 11:03:44 -08:00
392f6fa0b5 Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 41s
2025-01-22 01:08:56 -08:00
09421ee792 Update server.py
All checks were successful
/ check (push) Successful in 25s
2025-01-22 00:39:03 -08:00
15ab96dc58 Update server.py
Some checks failed
/ check (push) Failing after 11s
2025-01-22 00:25:30 -08:00
3746cb9b21 Typo
All checks were successful
/ check (push) Successful in 31s
2025-01-21 23:25:22 -08:00
591b857305 Fix bug with MOTD, fix bug on JOIN with Revolution IRC
All checks were successful
/ check (push) Successful in 18s
2025-01-21 23:15:08 -08:00
43bd7d8fd7 Update modules/sqlite_local.py
All checks were successful
/ check (push) Successful in 9s
2025-01-21 21:04:45 -08:00
0dbbd0febb Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 10s
2025-01-21 21:01:31 -08:00
a40716a33d Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 10s
2025-01-21 21:00:41 -08:00
90eb9a1e94 Update modules/sqlite_local.py
All checks were successful
/ check (push) Successful in 9s
2025-01-21 20:58:07 -08:00
dde77a9c3d Update modules/sqlite_local.py
All checks were successful
/ check (push) Successful in 9s
2025-01-21 20:56:27 -08:00
afb9849f85 Update modules/sqlite_local.py
All checks were successful
/ check (push) Successful in 9s
2025-01-21 20:54:50 -08:00
28ca9b8ee2 Update modules/sqlite_local.py
All checks were successful
/ check (push) Successful in 8s
2025-01-21 20:53:15 -08:00
78fc11ca7e Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 9s
2025-01-21 20:51:44 -08:00
bf79a94694 Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 9s
2025-01-21 20:49:43 -08:00
2d03559ceb Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 9s
2025-01-21 20:48:35 -08:00
5e2c6f852f Update modules/sqlite_local.py
All checks were successful
/ check (push) Successful in 9s
2025-01-21 20:46:41 -08:00
71c1acca96 Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 12s
2025-01-21 20:44:55 -08:00
dd1e2d0577 Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 11s
2025-01-21 20:44:26 -08:00
59500b21e0 Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 10s
2025-01-21 20:43:56 -08:00
7794ed874a Update modules/pawserv.py
Some checks failed
/ check (push) Has been cancelled
2025-01-21 20:43:22 -08:00
7f42363072 Update modules/sqlite_local.py
All checks were successful
/ check (push) Successful in 10s
2025-01-21 20:16:48 -08:00
8865303d0c Update modules/sqlite_local.py
All checks were successful
/ check (push) Successful in 12s
2025-01-21 19:57:54 -08:00
692960076d Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 12s
2025-01-21 19:56:00 -08:00
01fec7370b Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 12s
2025-01-21 19:46:13 -08:00
53d64a7e0d Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 10s
2025-01-21 19:33:00 -08:00
5ef65c3f38 Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 9s
2025-01-21 19:30:39 -08:00
1a885498e2 Update server.py
All checks were successful
/ check (push) Successful in 9s
2025-01-21 19:28:50 -08:00
f3d2af23a0 Update server.py
All checks were successful
/ check (push) Successful in 10s
2025-01-21 19:27:58 -08:00
fbb3e074cb Update server.py
All checks were successful
/ check (push) Successful in 10s
2025-01-21 19:26:35 -08:00
c0fce8a325 Update server.py
All checks were successful
/ check (push) Successful in 10s
2025-01-21 19:25:10 -08:00
f1f4f5688a Update server.py
All checks were successful
/ check (push) Successful in 9s
2025-01-21 19:22:38 -08:00
529a026814 Update server.py
All checks were successful
/ check (push) Successful in 11s
2025-01-21 19:21:48 -08:00
ee1deedf71 Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 9s
2025-01-21 19:20:15 -08:00
efe35d5be5 Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 12s
2025-01-21 19:19:52 -08:00
2f555b39f3 Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 10s
2025-01-21 19:19:37 -08:00
a957dccafd Update modules/pawserv.py
All checks were successful
/ check (push) Successful in 14s
2025-01-21 19:17:07 -08:00
da753090c7 Add modules/pawserv.py
Some checks failed
/ check (push) Failing after 10s
2025-01-21 19:16:24 -08:00
5ac6125f5c Update modules/sqlite_local.py
All checks were successful
/ check (push) Successful in 17s
2025-01-21 19:03:21 -08:00
d4f1f21916 Update modules/sqlite_local.py
All checks were successful
/ check (push) Successful in 13s
2025-01-21 19:01:09 -08:00
500a3cab30 Update server.py
All checks were successful
/ check (push) Successful in 9s
2025-01-21 18:58:46 -08:00
f1b739c5d8 Update server.py
Some checks failed
/ check (push) Failing after 15s
2025-01-21 18:57:42 -08:00
21287e8e89 Merge pull request 'Update modules/sqlite_local.py' (!1) from swee-patch-1 into main
All checks were successful
/ check (push) Successful in 10s
Reviewed-on: #1
2025-01-21 18:39:15 -08:00
6d53be98c6 Update server.py
All checks were successful
/ check (push) Successful in 13s
2025-01-21 18:27:58 -08:00
99804fed7f Update server.py
Some checks failed
/ check (push) Failing after 12s
2025-01-21 18:21:36 -08:00
9 changed files with 787 additions and 476 deletions

View file

@ -7,4 +7,4 @@ jobs:
with: with:
additional: python3 additional: python3
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- run: python -m py_compile server.py - run: python -m compileall .

View file

@ -9,9 +9,6 @@ host: 127.0.0.1
# The identifier for this server, such as the location (Used in whois) # The identifier for this server, such as the location (Used in whois)
identifier: somewhere in the universe identifier: somewhere in the universe
# The nickserv account to automatically be IRC opped
admin-nick: admin
# The path of the data file to be used by NickServ, ChanServ, etc # The path of the data file to be used by NickServ, ChanServ, etc
# This MUST be a file path. # This MUST be a file path.
# Recommended to use a .db extension because it is an SQLite database # Recommended to use a .db extension because it is an SQLite database
@ -45,9 +42,21 @@ ban-provider: /path/to/bans.txt
# ban-provider: sql # ban-provider: sql
# Mail server settings for PawServ
smtp_host: smtp.example.com
smtp_port: 25
smtp_starttls: off
smtp_username: pawserv@example.com
smtp_password: examplePassword
# If you setup a webchat, enter the passphrase for it.
webirc_pass: helloworld
# Use of modules in the /modules folder, or in an absolute path specified. # Use of modules in the /modules folder, or in an absolute path specified.
# You want your protection modules BEFORE the ban engine. # You want your protection modules BEFORE the ban engine.
modules: modules:
- sqlite_local - sqlite_local
- botnet_protect - botnet_protect
- ban_engine - ban_engine
- pawserv

View file

@ -2,10 +2,13 @@ import threading
__ircat_type__ = "allsocket" __ircat_type__ = "allsocket"
__ircat_requires__ = ["ban-provider"] __ircat_requires__ = ["ban-provider"]
__ircat_giveme__ = ["sql"] # Only command and allsocket have these. __ircat_giveme__ = ["sql"] # Only command and allsocket have these.
__ircat_fakechannels__ = {"#IRCATSUCKS": "WHATEVER YOU DO, DO NOT JOIN IF YOU ARE HUMAN"} __ircat_fakechannels__ = {"#IRCATSUCKS": "B0tn3t pr0t3ct10n, do not join."} # Fake channels that plugins control.
class IRCatModule: class IRCatModule:
sus_strings = [ sus_strings = [
" .''." # Latest Supernets spambot! # Known SupernetS botnet texts
# Contribute here: https://discuss.swee.codes/t/61
" .''.", # 2025 new year
"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣤⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀" # "The United States of America" LATEST
] ]
memory = {} # {nick: {channel: trustlevel}} one can also be {nick: True} if it is whitelisted for the session. memory = {} # {nick: {channel: trustlevel}} one can also be {nick: True} if it is whitelisted for the session.
useSQLengine = False useSQLengine = False
@ -15,27 +18,27 @@ class IRCatModule:
self.useSQLengine = True self.useSQLengine = True
self.SQLengine = sql self.SQLengine = sql
def ban(self, ip): def ban(self, ip):
del self.memory[ip] del self.memory[ip] # Forget this all happened
# Add the ban
if self.useSQLengine: if self.useSQLengine:
cur = self.SQLengine.conn.cursor() self.SQLengine.addban(ip, "Botnet detected!") # Use the SQL provider if {'ban-provider': 'sql'}
else: else:
open(self.ban_provider, "a").write(f"\n{ip} Botnet detected!") open(self.ban_provider, "a").write(f"\n{ip} Botnet detected!") # Else, write on the banfile.
raise Exception("Botnet detected!") raise Exception("Botnet detected!") # Kill the connection
def onSocket(self, ip, socket, value, cachedNick=None, validated=False): def onSocket(self, ip, socket, value, cachedNick=None, validated=False):
if cachedNick != None: if cachedNick != None:
print(value) print(value)
if "JOIN" in value: if "JOIN" in value:
target = value.split(" ")[1] target = value.split(" ")[1]
self.memory[ip] = 1 # 1: Just joined the channel, continue observing. self.memory[ip] = 1 # 1: Just joined the channel, continue observing.
print("Autoban> Somebody joined " + target)
if target.lower() == "#ircatsucks": if target.lower() == "#ircatsucks":
self.ban(ip) self.ban(ip) # Ruh roh
elif "PRIVMSG" in value: elif "PRIVMSG" in value:
if not (ip in self.memory and self.memory[ip] == 0): if not (ip in self.memory and self.memory[ip] == 0): # Continue observing
target = value.split(" ")[1] target = value.split(" ")[1]
content = " ".join(value.split(" ")[2:])[1:] content = " ".join(value.split(" ")[2:])[1:]
if content in self.sus_strings: if content in self.sus_strings:
if ip in self.memory: if ip in self.memory: # Hey stinky! YOU'RE BANNED
if self.memory[ip] == 1: if self.memory[ip] == 1:
self.ban(ip) self.ban(ip)
else: else:

View file

@ -1,12 +1,55 @@
import requests, os import os, traceback
from cryptography.fernet import Fernet
from cloudflare import Cloudflare # Please make sure you install this module from pip, not package manager.
__ircat_type__ = "sql.provider" # The type of module __ircat_type__ = "sql.provider" # The type of module
__ircat_requires__ = ["cf_accountid", "cf_apitoken", "cf_d1database"] # The required config.yml entries. __ircat_requires__ = ["cf_accountid", "cf_apitoken", "cf_d1database", "fernet-key"] # The required config.yml entries.
class broker: class broker:
def __init__(self, cf_accountid, cf_apitoken, cf_d1database): def __init__(self, cf_accountid:str, cf_apitoken:str, cf_d1database:str, fernet_key:str):
self.account_id = cf_accountid self.account_id = cf_accountid
self.api_token = cf_apitoken self.api_token = cf_apitoken
self.base_url = f"https://api.cloudflare.com/client/v4/accounts/{self.account_id}/d1/database" self.database = cf_d1database
self.headers = { self.client = Cloudflare(api_token=cf_apitoken)
"Content-Type": "application/json", self.fnet = Fernet(fernet_key)
"Authorization": f"Bearer {self.api_token}" self.client.d1.database.query(
} database_id=self.database,
account_id=self.account_id,
sql="CREATE table IF NOT EXISTS bans (ip varchar(255), reason varchar(255)); CREATE table IF NOT EXISTS nickserv (user varchar(255), modes varchar(255), hash varchar(255), email varchar(255)); CREATE table IF NOT EXISTS groups (name varchar(255), owner varchar(255)); CREATE table IF NOT EXISTS chanserv (name varchar(255), modes varchar(255), params varchar(255), owner varchar(255), usermodes varchar(255), optimodes varchar(255))",
)
def cfexec(self, command:str, params:list):
query = self.client.d1.database.query(
database_id=self.database,
account_id=self.account_id,
sql=command,
params=params
)
return query[0].results
def parse2sqlite(self, results):
temp = []
for k, v in results.items():
temp.append(v)
return temp
def nickserv_identify(self, nick, password:str):
f = self.cfexec("SELECT * FROM groups WHERE name=?;", [nick])
if len(f) != 0:
nick = f[0]["owner"]
e = self.cfexec("SELECT * FROM nickserv WHERE user=?;", [nick])
if len(e) == 0:
return False
else:
try:
return self.parse2sqlite(e[0]) if self.fnet.decrypt(bytes(e[0]["hash"], "UTF-8")).decode() == password else False
except:
print(traceback.format_exc())
return False
def nickserv_register(self, nick, password, email):
hashed = self.fnet.encrypt(bytes(password, "UTF-8")).decode()
e = self.cfexec("INSERT INTO nickserv values(?, 'iw', ?, ?);", [nick, hashed, email])
def nickserv_isexist(self, nick):
e = self.cfexec("SELECT * FROM nickserv WHERE user=?;", [nick])
f = self.cfexec("SELECT * FROM groups WHERE name=?;", [nick])
return len(e) != 0 or len(f) != 0
def nickserv_group(self, nick, account):
self.cfexec("INSERT INTO groups VALUES (?, ?);", [nick.lower(), account.lower()])
def nickserv_drop(self, account):
self.cfexec("DELETE FROM nickserv WHERE user=?;", [account.lower()])
self.cfexec("DELETE FROM groups WHERE owner=?;", [account.lower()])

123
modules/pawserv.py Normal file
View file

@ -0,0 +1,123 @@
# Replacement for services bots.
import traceback, smtplib, uuid, ssl
__ircat_type__ = "command"
__ircat_requires__ = ["name", "smtp_host", "smtp_port", "smtp_starttls", "smtp_username", "smtp_password", "host"]
__ircat_giveme__ = ["sql"] # Only command and allsocket have these.
__ircat_fakeusers__ = {
"NickServ": {
"host": "PawServ",
"username": "Meow",
"realname": "PawServ plugin - Identification bot",
"modes": "iw",
"away": False,
"identified": False,
"ssl": False
},
"ChanServ": {
"host": "PawServ",
"username": "Meow",
"realname": "PawServ plugin - Channel management bot",
"modes": "iw",
"away": False,
"identified": False,
"ssl": False
}
}
class IRCatModule:
def __init__(self, sql, smtp_host, smtp_port, smtp_starttls, smtp_username, smtp_password, name, host):
self.sql = sql
self.smtp_server = smtp_host
self.smtp_port = smtp_port
self.smtp_starttls = smtp_starttls
self.smtp_username = smtp_username
self.smtp_password = smtp_password
self.net_name = name
self.hostname = host
self.memory = {} # {nick: [authtoken, password, email]}
print("PawServ loaded!")
def command(self, command, args, ip, nick, connection, user):
try:
if command == "NICKSERV" or (command == "PRIVMSG" and args[0].lower() == "nickserv") or command == "PASS":
if command == "PASS":
command = "NICKSERV"
args = ["IDENTIFY", args[0]]
if command == "PRIVMSG":
args = args[1:]
args[0] = args[0][1:] if args[0][0] == ":" else args[0]
if len(args) > 0 and args[0].lower() == "verify":
if len(args) == 3:
if args[1].lower() in self.memory:
if args[2] == self.memory[args[1].lower()][0]:
self.sql.nickserv_register(nick=args[1].lower(), password=self.memory[args[1].lower()][1], email=self.memory[args[1].lower()][2])
nck = args[1].lower()
connection.sendall(bytes(f":NickServ!Meow@PawServ NOTICE {nick} :Done, you may now identify as {nck}.\r\n", "UTF-8"))
del self.memory[args[1].lower()]
else:
connection.sendall(bytes(f":NickServ!Meow@PawServ NOTICE {nick} :Invalid verification.\r\n", "UTF-8"))
else:
connection.sendall(bytes(f":NickServ!Meow@PawServ NOTICE {nick} :Nickname doesn't exist, try registering again?\r\n", "UTF-8"))
else:
connection.sendall(bytes(f":NickServ!Meow@PawServ NOTICE {nick} :Invalid verification.\r\n", "UTF-8"))
elif len(args) > 0 and args[0].lower() == "group":
if len(args) == 1:
if user["identified"]:
if not self.sql.nickserv_isexist(nick.lower()):
self.sql.nickserv_group(nick, user["identusername"])
else:
connection.sendall(bytes(f":NickServ!Meow@PawServ NOTICE {nick} :Nickname {nick} already exists.\r\n", "UTF-8"))
else:
connection.sendall(bytes(f":NickServ!Meow@PawServ NOTICE {nick} :You are not logged in.\r\n", "UTF-8"))
else:
connection.sendall(bytes(f":NickServ!Meow@PawServ NOTICE {nick} :Does not requre arguments\r\n", "UTF-8"))
elif len(args) > 0 and args[0].lower() == "register":
if not user["identified"]:
if len(args) == 3:
if not self.sql.nickserv_isexist(nick.lower()):
if not nick in self.memory:
context = ssl.create_default_context()
token = str(uuid.uuid4())
message = f"Subject: {self.net_name} Account Verification\n\nHi,\nIt appears you have tried to register an account ({nick}) with this email on {self.net_name},\nIf you did not register an account, feel free to delete this email.\nIf you did, use this command:\n/nickserv verify {nick} {token}"
with smtplib.SMTP(self.smtp_server, self.smtp_port) as server:
server.ehlo()
if self.smtp_starttls:
server.starttls(context=context)
server.ehlo()
server.login(self.smtp_username, self.smtp_password)
server.sendmail(self.smtp_username, args[2], message)
self.memory[nick.lower()] = [token, args[1], args[2]]
connection.sendall(bytes(f":NickServ!Meow@PawServ NOTICE {nick} :Email sent, please verify.\r\n", "UTF-8"))
else:
connection.sendall(bytes(f":NickServ!Meow@PawServ NOTICE {nick} :A verification is already pending.\r\n", "UTF-8"))
else:
connection.sendall(bytes(f":NickServ!Meow@PawServ NOTICE {nick} :The user {nick} already exists.\r\n", "UTF-8"))
else:
connection.sendall(bytes(f":NickServ!Meow@PawServ NOTICE {nick} :Needs 3 arguments, nickname, password, and email.\r\n", "UTF-8"))
else:
connection.sendall(bytes(f":NickServ!Meow@PawServ NOTICE {nick} :You're already logged in.\r\n", "UTF-8"))
elif len(args) > 0 and args[0].lower() == "identify":
if not user["identified"]:
nck = nick if len(args) == 2 else args[2]
temp = self.sql.nickserv_identify(nick=nck.lower(), password=args[1])
if temp != False:
hostmask = user["host"]
connection.sendall(bytes(f":NickServ!Meow@PawServ NOTICE {nick} :You are now identified as {nck}.\r\n", "UTF-8"))
connection.sendall(bytes(f":{self.hostname} 900 {nick} {hostmask} {nck} :You are now logged in as {nck}.\r\n", "UTF-8"))
return {"success": True, "identify": temp}
else:
if nick.lower() in self.memory:
connection.sendall(bytes(f":NickServ!Meow@PawServ NOTICE {nick} :Your account isn't verified, please verify now.\r\n", "UTF-8"))
else:
connection.sendall(bytes(f":NickServ!Meow@PawServ NOTICE {nick} :Invalid username/password.\r\n", "UTF-8"))
else:
connection.sendall(bytes(f":NickServ!Meow@PawServ NOTICE {nick} :You're already logged in.\r\n", "UTF-8"))
else:
connection.sendall(bytes(f":NickServ!Meow@PawServ NOTICE {nick} :NickServ Usage:\r\n","UTF-8"))
connection.sendall(bytes(f":NickServ!Meow@PawServ NOTICE {nick} :IDENTIFY pass <nick> - Identifies your nickname\r\n","UTF-8"))
connection.sendall(bytes(f":NickServ!Meow@PawServ NOTICE {nick} :REGISTER pass email - Register your nickname\r\n","UTF-8"))
connection.sendall(bytes(f":NickServ!Meow@PawServ NOTICE {nick} :GROUP - Allows you to sign in to your account with different nicknames\r\n","UTF-8"))
return {"success": True}
else:
return {"success": False}
except:
print(traceback.format_exc())
return {"success": False}

View file

@ -1,6 +1,6 @@
# IRCat module for local SQLite database (default) # IRCat module for local SQLite database (default)
import sqlite3, os, traceback import sqlite3, os, traceback
from cryptography import Fernet from cryptography.fernet import Fernet
__ircat_type__ = "sql.provider" # The type of module __ircat_type__ = "sql.provider" # The type of module
__ircat_requires__ = ["data-path", "fernet-key"] # The required config.yml entries. __ircat_requires__ = ["data-path", "fernet-key"] # The required config.yml entries.
class broker: class broker:
@ -8,22 +8,46 @@ class broker:
if not os.path.isfile(data_path): if not os.path.isfile(data_path):
print("Creating database file...") print("Creating database file...")
open(data_path, "w").write("") open(data_path, "w").write("")
self.conn = sqlite3.connect(data_path) self.conn = sqlite3.connect(data_path, check_same_thread=False)
self.fnet = Fernet(fernet_key) self.fnet = Fernet(fernet_key)
db = self.conn.cursor() db = self.conn.cursor()
db.execute("""CREATE table IF NOT EXISTS bans (ip varchar(255), reason varchar(255)""") db.execute("""CREATE table IF NOT EXISTS bans (ip varchar(255), reason varchar(255))""")
db.execute("""CREATE table IF NOT EXISTS nickserv (user varchar(255), modes varchar(255), hash varchar(255), cloak varchar(255))""") db.execute("""CREATE table IF NOT EXISTS nickserv (user varchar(255), modes varchar(255), hash varchar(255), email varchar(255))""")
db.execute("""CREATE table IF NOT EXISTS groups (name varchar(255), owner varchar(255))""") db.execute("""CREATE table IF NOT EXISTS groups (name varchar(255), owner varchar(255))""")
db.execute("""CREATE table IF NOT EXISTS chanserv (name varchar(255), modes varchar(255), params varchar(255), owner varchar(255), usermodes varchar(255), optimodes varchar(255))""") db.execute("""CREATE table IF NOT EXISTS chanserv (name varchar(255), modes varchar(255), params varchar(255), owner varchar(255), usermodes varchar(255), optimodes varchar(255))""")
def nickserv_identify(self, nick, password:str): def nickserv_identify(self, nick, password:str):
db = self.conn.cursor() db = self.conn.cursor()
db.execute("SELECT * FROM groups WHERE name=?", [nick])
f = db.fetchall()
if f != []:
nick = f[0][1]
db.execute("SELECT * FROM nickserv WHERE user=?;", [nick]) db.execute("SELECT * FROM nickserv WHERE user=?;", [nick])
e = db.fetchall() e = db.fetchall()
if e == []: if e == []:
return False return False
else: else:
try: try:
return e[0] if self.fnet.decrypt(e[0][2]) == password else False return e[0] if self.fnet.decrypt(bytes(e[0][2], "UTF-8")).decode() == password else False
except: except:
print(traceback.format_exc()) print(traceback.format_exc())
return False return False
def nickserv_register(self, nick, password, email):
hashed = self.fnet.encrypt(bytes(password, "UTF-8")).decode()
db = self.conn.cursor()
db.execute("INSERT INTO nickserv values(?, 'iw', ?, ?);", [nick, hashed, email])
self.conn.commit()
def nickserv_isexist(self, nick):
db = self.conn.cursor()
db.execute("SELECT * FROM nickserv WHERE user=?;", [nick.lower()])
e = db.fetchall()
db.execute("SELECT * FROM groups WHERE name=?;", [nick.lower()])
f = db.fetchall()
return e != [] or f != []
def nickserv_group(self, nick, account):
db = self.conn.cursor()
db.execute("INSERT INTO groups VALUES (?, ?);", [nick.lower(), account.lower()])
self.conn.commit()
def nickserv_drop(self, account):
db = self.conn.cursor()
db.execute("DELETE FROM nickserv WHERE user=?;", [account.lower()])
db.execute("DELETE FROM groups WHERE owner=?;", [account.lower()])

View file

@ -1,2 +1,4 @@
cloudflare>=4.0.0
requests requests
PyOpenSSL
pyyaml pyyaml

287
server.py
View file

@ -1,8 +1,9 @@
#!/usr/bin/python3 #!/usr/bin/python3
__version__ = "0.0.4" __version__ = "0.0.5"
print(f"Codename IRCat v{__version__}") print(f"Codename IRCat v{__version__}")
print("Welcome! /ᐠ ˵> ⩊ <˵マ") print("Welcome! /ᐠ ˵> ⩊ <˵マ")
import socket, ssl, time, threading, traceback, sys, subprocess, yaml, sqlite3, os, bcrypt, importlib import socket, time, ssl, threading, traceback, sys, subprocess, yaml, sqlite3, os, importlib
from OpenSSL import SSL
from requests import get from requests import get
if not len(sys.argv) == 2: if not len(sys.argv) == 2:
print("IRCat requires the following arguments: config.yml") print("IRCat requires the following arguments: config.yml")
@ -23,11 +24,50 @@ This server doesn't have a MOTD in its configuration, or is invalid."""
motd_file = None motd_file = None
ping_timeout = 255 ping_timeout = 255
restrict_ip = '' restrict_ip = ''
def isalphanumeric(text:str):
return False
def getident(hostt:str, clientport:int, ssll:bool):
try:
identsender = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
identsender.settimeout(5)
responsee = ""
try:
identsender.connect((hostt, 113))
except Exception as ex:
return {"success": False, "response": f"Could not connect to your ident server: {ex}"}
serverport = "6697" if ssll else "6667"
try:
identsender.sendall(bytes(f"{clientport}, {serverport}\r\n", "UTF-8"))
responsee = identsender.recv(2048).decode().replace(" ", "").replace("\r", "").replace("\n", "")
print(responsee)
except Exception as ex:
return {"success": False, "response": f"Could not send packets to your server: {ex}"}
if "ERROR:NO-USER" in responsee:
return {"success": False, "response": "No user was found by the server."}
elif "ERROR:" in responsee:
return {"success": False, "response": "The ident server had an error."}
elif responsee == "":
return {"success": False, "response": "The connection was closed."}
else:
print(responsee.split(",")[0])
print(responsee.split(",")[1].split(":")[0])
if responsee.split(",")[0] != str(clientport):
return {"success": False, "response": "The ident server sent an invalid client port."}
elif responsee.split(",")[1].split(":")[0] != serverport:
return {"success": False, "response": "The ident server doesn't know what the server port is."}
else:
return {"success": True, "response": responsee.split(",")[1].split(":")[3]}
return {"success": False, "response": "Unknown error."}
except:
print(traceback.format_exc())
return {"success": False, "response": "Unknown error."}
global mods global mods
mods = {"sql_provider": None, "command": [], "allsocket": []} mods = {"sql_provider": None, "command": [], "allsocket": [], "identified": False, "ssl": False}
with open(sys.argv[1], 'r') as file: with open(sys.argv[1], 'r') as file:
global data global data2
data = yaml.safe_load(file) data = yaml.safe_load(file)
data2 = data
try: server = data["host"] try: server = data["host"]
except: print("using fallback server address") except: print("using fallback server address")
try: displayname = data["name"] try: displayname = data["name"]
@ -109,11 +149,14 @@ for i in mods['allsocket']:
print(i.__ircat_fakechannels__) print(i.__ircat_fakechannels__)
topic_list = {**topic_list, **i.__ircat_fakechannels__} topic_list = {**topic_list, **i.__ircat_fakechannels__}
for j, v in i.__ircat_fakechannels__.items(): for j, v in i.__ircat_fakechannels__.items():
channels_list[j] = ["NickServ"] channels_list[j] = ["CatServ"]
except Exception as ex: except Exception as ex:
print(str(ex)) print(str(ex))
socketListeners.append(i.IRCatModule(**requires)) socketListeners.append(i.IRCatModule(**requires))
commandProviders = [] commandProviders = []
nickname_list = {} # Stores nicknames and the respective sockets
lower_nicks = {"catserv": "CatServ"} # Nicknames in lowercase
property_list = {"CatServ": {"host": "IRCatCore", "username": "Meow", "realname": "Updates bot", "modes": "iw", "away": False}} # Stores properties for active users and channels
for i in mods['command']: for i in mods['command']:
requires = {} requires = {}
for j in i.__ircat_requires__: for j in i.__ircat_requires__:
@ -124,7 +167,6 @@ for i in mods['command']:
print(i.__ircat_fakeusers__) print(i.__ircat_fakeusers__)
property_list = {**property_list, **i.__ircat_fakeusers__} property_list = {**property_list, **i.__ircat_fakeusers__}
for j, v in i.__ircat_fakeusers__.items(): for j, v in i.__ircat_fakeusers__.items():
nickname_list.append(j)
lower_nicks[j.lower()] = j lower_nicks[j.lower()] = j
except Exception as ex: except Exception as ex:
print(str(ex)) print(str(ex))
@ -135,24 +177,18 @@ sockets_ssl = {}
for i in restrict_ip.split(" "): for i in restrict_ip.split(" "):
sockets[i] = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sockets[i] = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sockets[i].setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sockets[i].setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sockets[i].settimeout(None)
sockets[i].bind((i,6667)) sockets[i].bind((i,6667))
sockets[i].listen(1) sockets[i].listen(1)
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.options |= ssl.OP_NO_TLSv1 # Disable TLS 1.0
context.options |= ssl.OP_NO_TLSv1_1 # Disable TLS 1.1
if ssl_option: if ssl_option:
print(f"Loading SSL cert {ssl_cert} with key {ssl_pkey}")
context.load_cert_chain(ssl_cert, keyfile=ssl_pkey)
for i in restrict_ip.split(" "): for i in restrict_ip.split(" "):
sockets_ssl[i] = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sockets_ssl[i] = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sockets_ssl[i].setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sockets_ssl[i].setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sockets_ssl[i].settimeout(None)
sockets_ssl[i].bind((i,6697)) sockets_ssl[i].bind((i,6697))
sockets_ssl[i].listen(1) sockets_ssl[i].listen(1)
opened=True opened=True
nickname_list = {} # Stores nicknames and the respective sockets
lower_nicks = {"gitserv": "GitServ"} # Nicknames in lowercase
lower_chans = {} # Channel names in lowercase lower_chans = {} # Channel names in lowercase
property_list = {"GitServ": {"host": "IRCatCore", "username": "IRCat", "realname": "Updates bot", "modes": "iw", "away": False}} # Stores properties for active users and channels
def pinger(nick, connection): def pinger(nick, connection):
global property_list global property_list
while nick in property_list: while nick in property_list:
@ -164,6 +200,7 @@ def pinger(nick, connection):
try: try:
connection.sendall(bytes(f"PING {server}\r\n","UTF-8")) connection.sendall(bytes(f"PING {server}\r\n","UTF-8"))
except Exception as ex: except Exception as ex:
print(traceback.format_exc())
property_list[nick]["cause"] = "Send error: " + str(ex) property_list[nick]["cause"] = "Send error: " + str(ex)
print("SHUTTING DOWN FOR " + nick) print("SHUTTING DOWN FOR " + nick)
connection.shutdown(socket.SHUT_WR) connection.shutdown(socket.SHUT_WR)
@ -176,7 +213,7 @@ def pinger(nick, connection):
connection.shutdown(socket.SHUT_WR) connection.shutdown(socket.SHUT_WR)
connection.close() connection.close()
break break
def session(connection, client, ip, ssl=False): def session(connection, client, ip, isssl=False):
global property_list global property_list
global channels_list global channels_list
global nickname_list global nickname_list
@ -185,31 +222,58 @@ def session(connection, client, ip, ssl=False):
ready = False # If the client gave the server a USER packet ready = False # If the client gave the server a USER packet
finished = False # If the server gave the client its information, indicating it's ready. finished = False # If the server gave the client its information, indicating it's ready.
username = "oreo" # Username/ident specified by client username = "oreo" # Username/ident specified by client
rident = "~oreo"
hostname = "" # Hostname, can be IP or domain hostname = "" # Hostname, can be IP or domain
realname = "realname" # Realname specified by client realname = "realname" # Realname specified by client
safe_quit = False # If the client safely exited, or if the server should manually drop the connection safe_quit = False # If the client safely exited, or if the server should manually drop the connection
cause = "Unknown" # The cause of the unexpected exit cause = "Unknown" # The cause of the unexpected exit
usesIRCv3 = False
CAPEND = False
clident = None
pendingCommands = "" # list of commands that were executed before verification
unfinished = False
textt = ""
try: try:
print("Connected to client IP: {}".format(client)) print("Connected to client IP: {}".format(client))
connection.sendall(bytes(f":{server} NOTICE * :*** Looking for your hostname...\r\n","UTF-8")) connection.sendall(bytes(f":{server} NOTICE * :*** Looking for your hostname...\r\n","UTF-8"))
connection.sendall(bytes(f":{server} NOTICE * :*** Checking your ident...\r\n","UTF-8"))
try: try:
hostname = socket.gethostbyaddr(client[0])[0] hostname = socket.gethostbyaddr(client[0])[0]
connection.sendall(bytes(f":{server} NOTICE * :*** Got it! {hostname}\r\n","UTF-8")) connection.sendall(bytes(f":{server} NOTICE * :*** Got hostname! {hostname}\r\n","UTF-8"))
except: except:
hostname = client[0] hostname = client[0]
connection.sendall(bytes(f":{server} NOTICE * :*** Oof! Can't find your hostname, using IP...\r\n","UTF-8")) connection.sendall(bytes(f":{server} NOTICE * :*** Oof! Can't find your hostname, using IP...\r\n","UTF-8"))
try:
identQuery = getident(hostname, client[1], isssl)
responseee = identQuery["response"]
print(identQuery)
if not identQuery["success"]:
connection.sendall(bytes(f":{server} NOTICE * :*** Uhm, Couldn't find your ident: {responseee}\r\n","UTF-8"))
else:
connection.sendall(bytes(f":{server} NOTICE * :*** Got ident! {responseee}\r\n","UTF-8"))
clident = responseee
rident = responseee
except:
print(traceback.format_exc())
connection.sendall(bytes(f":{server} NOTICE * :*** Uhm, Couldn't find your ident: Unknown error.\r\n","UTF-8"))
while True: while True:
try: try:
data = connection.recv(2048) data = connection.recv(2048)
if not data: if not data:
cause = "Remote host closed the connection" cause = "Remote host closed the connection"
break break
except ssl.SSLEOFError:
pass
except ssl.SSLZeroReturnError:
cause = "Remote host closed the connection"
break
except Exception as ex: except Exception as ex:
cause = "Read error: " + str(ex) cause = "Read error: " + str(ex)
break break
print("Received data: {}".format(data)) print("Received data: {}".format(data))
try: try:
textt = data.decode() textt += data.decode()
if textt[-1] == "\n":
for text in textt.replace("\r", "").split("\n"): for text in textt.replace("\r", "").split("\n"):
for i in socketListeners: for i in socketListeners:
if "onSocket" in dir(i): if "onSocket" in dir(i):
@ -221,7 +285,7 @@ def session(connection, client, ip, ssl=False):
pass pass
if command == "NICK" and not finished: if command == "NICK" and not finished:
pending = text.split(" ")[1] pending = text.split(" ")[1]
if pending[0] == ":": pending[1:] if pending[0] == ":": pending = pending[1:]
if "!" in pending or ":" in pending or "#" in pending or "*" in pending: if "!" in pending or ":" in pending or "#" in pending or "*" in pending:
connection.sendall(bytes(f":{server} 432 * {pending} :Erroneus nickname\r\n","UTF-8")) connection.sendall(bytes(f":{server} 432 * {pending} :Erroneus nickname\r\n","UTF-8"))
pending = "*" pending = "*"
@ -237,18 +301,38 @@ def session(connection, client, ip, ssl=False):
realname = " ".join(text.split(" ")[4:])[1:] realname = " ".join(text.split(" ")[4:])[1:]
ready = True ready = True
elif command == "CAP": elif command == "CAP":
if args[0] == "LS": #usesIRCv3 = True
if args[0].upper() == "LS":
connection.sendall(bytes(f":{server} CAP * LS :ircat.xyz/foo\r\n", "UTF-8")) connection.sendall(bytes(f":{server} CAP * LS :ircat.xyz/foo\r\n", "UTF-8"))
elif (ready and already_set) and not finished: elif args[0].upper() == "REQ":
if args[1].lower() == ":sasl":
pass
#connection.sendall(f":{server} CAP * ACK :sasl")
elif args[0].upper() == "END":
CAPEND = True
elif command == "WEBIRC" and not finished:
try:
if args[0] == data2["webirc_pass"]:
hostname = args[2]
client = (args[3], client[1])
connection.sendall(bytes(f":{server} NOTICE * :*** WebIRC detected, welcome to IRC!\r\n", "UTF-8"))
if hostname != client[0]:
connection.sendall(bytes(f":{server} NOTICE * :*** Got WebIRC hostname! {hostname}\r\n", "UTF-8"))
except:
print(traceback.format_exc())
break
elif (ready and already_set) and (CAPEND if usesIRCv3 else True) and not finished:
cleanup_manual() cleanup_manual()
print(f"User {pending} successfully logged in.") print(f"User {pending} successfully logged in.")
nickname_list[pending] = connection nickname_list[pending] = connection
property_list[pending] = {"host": hostname, "username": username, "realname": realname, "modes": "iw", "last_ping": time.time(), "ping_pending": False, "away": False} property_list[pending] = {"host": hostname, "username": clident if clident != None else f"~{username }", "realname": realname, "modes": "iw", "last_ping": time.time(), "ping_pending": False, "away": False, "identified": False, "ssl": isssl}
lower_nicks[pending.lower()] = pending lower_nicks[pending.lower()] = pending
for i in socketListeners: for i in socketListeners:
if "onValidate" in dir(i): if "onValidate" in dir(i):
i.onValidate(socket=connection, ip=client[0]) i.onValidate(socket=connection, ip=client[0])
threading.Thread(target=pinger, args=[pending, connection]).start() threading.Thread(target=pinger, args=[pending, connection]).start()
if clident == None:
rident = f"~{username}"
connection.sendall(bytes(f":{server} 001 {pending} :Welcome to the {displayname} Internet Relay Chat Network {pending}\r\n", "UTF-8")) connection.sendall(bytes(f":{server} 001 {pending} :Welcome to the {displayname} Internet Relay Chat Network {pending}\r\n", "UTF-8"))
connection.sendall(bytes(f":{server} 002 {pending} :Your host is {server}[{ip}/6667], running version IRCat-v{__version__}\r\n", "UTF-8")) connection.sendall(bytes(f":{server} 002 {pending} :Your host is {server}[{ip}/6667], running version IRCat-v{__version__}\r\n", "UTF-8"))
connection.sendall(bytes(f":{server} 004 {pending} {server} IRCat-{__version__} iow ovmsitnlbkq\r\n", "UTF-8")) connection.sendall(bytes(f":{server} 004 {pending} {server} IRCat-{__version__} iow ovmsitnlbkq\r\n", "UTF-8"))
@ -267,38 +351,59 @@ def session(connection, client, ip, ssl=False):
motd = open(motd_file).read() motd = open(motd_file).read()
connection.sendall(bytes(f":{server} 375 {pending} :- {server} Message of the Day -\r\n", "UTF-8")) connection.sendall(bytes(f":{server} 375 {pending} :- {server} Message of the Day -\r\n", "UTF-8"))
for i in motd.rstrip().split("\n"): for i in motd.rstrip().split("\n"):
connection.sendall(bytes(f":{server} 376 {pending} :- {i}\r\n", "UTF-8")) connection.sendall(bytes(f":{server} 372 {pending} :- {i}\r\n", "UTF-8"))
connection.sendall(bytes(f":{server} 372 {pending} :End of /MOTD command\r\n", "UTF-8")) connection.sendall(bytes(f":{server} 376 {pending} :End of /MOTD command\r\n", "UTF-8"))
# End the MOTD # End the MOTD
connection.sendall(bytes(f":{pending} MODE {pending} +iw\r\n","UTF-8")) connection.sendall(bytes(f":{pending} MODE {pending} +iw\r\n","UTF-8"))
finished = True finished = True
elif command == "PING": elif command == "PING":
try:
e = text.split(" ")[1] e = text.split(" ")[1]
print("Replying with \"" + str([f":{server} PONG {server} :{e}\r\n"]) + "\"") e = f":{e}" if e[0] != ":" else e
connection.sendall(bytes(f":{server} PONG {server} :{e}\r\n","UTF-8")) connection.sendall(bytes(f":{server} PONG {server} {e}\r\n","UTF-8"))
elif command == "LIST": except:
connection.sendall(bytes(f":{server} 321 {pending} Channel :Users Name\r\n","UTF-8")) connection.sendall(bytes(f":{server} PONG {server}\r\n","UTF-8"))
for key, value in topic_list.items():
usersin = len(channels_list[key])
connection.sendall(bytes(f":{server} 322 {pending} {key} {usersin} :{value}\r\n","UTF-8"))
connection.sendall(bytes(f":{server} 323 {pending} :End of /LIST\r\n","UTF-8"))
elif command == "MOTD": elif command == "MOTD":
if motd_file != None: if motd_file != None:
motd = open(motd_file).read() motd = open(motd_file).read()
connection.sendall(bytes(f":{server} 375 {pending} :- {server} Message of the Day -\r\n", "UTF-8")) connection.sendall(bytes(f":{server} 375 {pending} :- {server} Message of the Day -\r\n", "UTF-8"))
for i in motd.rstrip().split("\n"): for i in motd.rstrip().split("\n"):
connection.sendall(bytes(f":{server} 376 {pending} :- {i}\r\n", "UTF-8")) connection.sendall(bytes(f":{server} 372 {pending} :- {i}\r\n", "UTF-8"))
connection.sendall(bytes(f":{server} 372 {pending} :End of /MOTD command\r\n", "UTF-8")) connection.sendall(bytes(f":{server} 376 {pending} :End of /MOTD command\r\n", "UTF-8"))
elif finished: elif finished:
pendingCommands += text
for comd in pendingCommands.split("\r\n"):
command = comd.split(" ")[0].upper()
args = comd.split(" ")[1:]
text = comd
processedExternally = False processedExternally = False
for i in commandProviders: for i in commandProviders:
if i.command(): cmdrun = i.command(command=command, args=args, nick=pending, ip=client[0], user=property_list[pending], connection=connection)
if cmdrun["success"]:
if "identify" in cmdrun:
if cmdrun["identify"] == "logout":
if "o" in property_list[pending]["modes"]:
connection.sendall(bytes(f":{pending} MODE {pending} -o\r\n","UTF-8"))
if not "i" in property_list[pending]["modes"]:
connection.sendall(bytes(f":{pending} MODE {pending} +i\r\n","UTF-8"))
if not "w" in property_list[pending]["modes"]:
connection.sendall(bytes(f":{pending} MODE {pending} +w\r\n","UTF-8"))
property_list[pending]["modes"] = "iw"
property_list[pending]["identified"] = False
else:
property_list[pending]["identified"] = True
property_list[pending]["identusername"] = cmdrun["identify"][0]
temp_mode = cmdrun["identify"][1]
property_list[pending]["modes"] = temp_mode
connection.sendall(bytes(f":{pending} MODE {pending} +{temp_mode}\r\n","UTF-8"))
processedExternally = True processedExternally = True
break break
if processedExternally: if processedExternally:
pass pass
elif command == "JOIN": elif command == "JOIN":
channels = text.split(" ")[1] channels = text.split(" ")[1]
if channels[0] == ":":
channels = channels[1:]
for channelt in channels.split(","): for channelt in channels.split(","):
channel = channelt.strip() channel = channelt.strip()
if channel.lower() in lower_chans: if channel.lower() in lower_chans:
@ -321,7 +426,7 @@ def session(connection, client, ip, ssl=False):
print(channels_list) print(channels_list)
for i in channels_list[channel]: for i in channels_list[channel]:
try: try:
nickname_list[i].sendall(bytes(f":{pending}!~{username}@{hostname} JOIN {channel}\r\n","UTF-8")) nickname_list[i].sendall(bytes(f":{pending}!{rident}@{hostname} JOIN {channel}\r\n","UTF-8"))
except: except:
pass pass
# Code re-used in the NAMES command # Code re-used in the NAMES command
@ -331,9 +436,15 @@ def session(connection, client, ip, ssl=False):
connection.sendall(bytes(f":{server} 353 {pending} = {channel} :{users}\r\n","UTF-8")) connection.sendall(bytes(f":{server} 353 {pending} = {channel} :{users}\r\n","UTF-8"))
connection.sendall(bytes(f":{server} 366 {pending} {channel} :End of /NAMES list.\r\n","UTF-8")) connection.sendall(bytes(f":{server} 366 {pending} {channel} :End of /NAMES list.\r\n","UTF-8"))
print("Successfully pre-loaded /NAMES list") print("Successfully pre-loaded /NAMES list")
elif command == "LIST":
connection.sendall(bytes(f":{server} 321 {pending} Channel :Users Name\r\n","UTF-8"))
for key, value in topic_list.items():
usersin = len(channels_list[key])
connection.sendall(bytes(f":{server} 322 {pending} {key} {usersin} :{value}\r\n","UTF-8"))
connection.sendall(bytes(f":{server} 323 {pending} :End of /LIST\r\n","UTF-8"))
elif command == "PONG": elif command == "PONG":
e = text.split(" ")[1] e = text.split(" ")[1]
if e == server: if e == server or e == f":{server}":
print(pending + " replied to PING.") print(pending + " replied to PING.")
property_list[pending]["last_ping"] = time.time() property_list[pending]["last_ping"] = time.time()
property_list[pending]["ping_pending"] = False property_list[pending]["ping_pending"] = False
@ -344,14 +455,14 @@ def session(connection, client, ip, ssl=False):
pass pass
else: else:
pending2 = text.split(" ")[1] pending2 = text.split(" ")[1]
if pending2[0] == ":": pending2[1:] if pending2[0] == ":": pending2 = pending2[1:]
if "!" in pending2 or ":" in pending2 or "#" in pending2 or "*" in pending2: if "!" in pending2 or ":" in pending2 or "#" in pending2 or "*" in pending2:
connection.sendall(bytes(f":{server} 432 {pending} {pending2} :Erroneus nickname\r\n","UTF-8")) connection.sendall(bytes(f":{server} 432 {pending} {pending2} :Erroneus nickname\r\n","UTF-8"))
elif pending2.lower() in lower_nicks: elif pending2.lower() in lower_nicks:
connection.sendall(bytes(f":{server} 433 {pending} {pending2} :Nickname is already in use.\r\n","UTF-8")) connection.sendall(bytes(f":{server} 433 {pending} {pending2} :Nickname is already in use.\r\n","UTF-8"))
else: else:
print("Sending nickname change...") print("Sending nickname change...")
connection.sendall(bytes(f":{pending}!~{username}@{hostname} NICK {pending2}\r\n","UTF-8")) connection.sendall(bytes(f":{pending}!{rident}@{hostname} NICK {pending2}\r\n","UTF-8"))
# Broadcast the nickname change # Broadcast the nickname change
done = [] done = []
for i, users in channels_list.items(): for i, users in channels_list.items():
@ -359,7 +470,7 @@ def session(connection, client, ip, ssl=False):
for j in users: for j in users:
if j != pending and j != pending2 and not j in done: if j != pending and j != pending2 and not j in done:
print("Broadcasting on " + j) print("Broadcasting on " + j)
nickname_list[j].sendall(bytes(f":{pending}!~{username}@{hostname} {text}\r\n","UTF-8")) nickname_list[j].sendall(bytes(f":{pending}!{rident}@{hostname} {text}\r\n","UTF-8"))
done.append(j) done.append(j)
# Replace the nickname # Replace the nickname
try: try:
@ -387,7 +498,7 @@ def session(connection, client, ip, ssl=False):
channel = text.split(" ")[1] channel = text.split(" ")[1]
for i in channels_list[channel]: for i in channels_list[channel]:
try: try:
nickname_list[i].sendall(bytes(f":{pending}!~{username}@{hostname} {text}\r\n","UTF-8")) nickname_list[i].sendall(bytes(f":{pending}!{rident}@{hostname} {text}\r\n","UTF-8"))
except: except:
pass pass
try: try:
@ -415,13 +526,13 @@ def session(connection, client, ip, ssl=False):
who_user = property_list[i]["username"] who_user = property_list[i]["username"]
who_realname = property_list[i]["realname"] who_realname = property_list[i]["realname"]
who_away = "G" if property_list[i]["away"] else "H" who_away = "G" if property_list[i]["away"] else "H"
connection.sendall(bytes(f":{server} 352 {pending} {channel} ~{who_user} {who_host} {server} {i} {who_away} :0 {who_realname}\r\n","UTF-8")) connection.sendall(bytes(f":{server} 352 {pending} {channel} {who_user} {who_host} {server} {i} {who_away} :0 {who_realname}\r\n","UTF-8"))
elif channel in nickname_list: elif channel in nickname_list:
who_host = property_list[channel]["host"] who_host = property_list[channel]["host"]
who_user = property_list[channel]["username"] who_user = property_list[channel]["username"]
who_realname = property_list[channel]["realname"] who_realname = property_list[channel]["realname"]
who_away = "G" if property_list[channel]["away"] else "H" who_away = "G" if property_list[channel]["away"] else "H"
connection.sendall(bytes(f":{server} 352 {pending} * ~{who_user} {who_host} {server} {channel} {who_away} :0 {who_realname}\r\n","UTF-8")) connection.sendall(bytes(f":{server} 352 {pending} * {who_user} {who_host} {server} {channel} {who_away} :0 {who_realname}\r\n","UTF-8"))
connection.sendall(bytes(f":{server} 315 {pending} {channel} :End of /WHO list.\r\n","UTF-8")) connection.sendall(bytes(f":{server} 315 {pending} {channel} :End of /WHO list.\r\n","UTF-8"))
elif command == "WHOIS": elif command == "WHOIS":
@ -435,17 +546,27 @@ def session(connection, client, ip, ssl=False):
who_user = property_list[target]["username"] who_user = property_list[target]["username"]
who_realname = property_list[target]["realname"] who_realname = property_list[target]["realname"]
who_host = property_list[target]["host"] who_host = property_list[target]["host"]
who_identified = property_list[target]["identified"]
who_ssl = property_list[target]["ssl"]
if who_identified:
who_identifying = property_list[target]["identusername"]
else:
who_identifying = None
try: try:
who_flags = property_list[target]["modes"] who_flags = property_list[target]["modes"]
except: except:
who_flags = None who_flags = None
connection.sendall(bytes(f":{server} 311 {pending} {target} ~{who_user} {who_host} * :{who_realname}\r\n","UTF-8")) connection.sendall(bytes(f":{server} 311 {pending} {target} {who_user} {who_host} * :{who_realname}\r\n","UTF-8"))
connection.sendall(bytes(f":{server} 312 {pending} {target} {server} :{identifier}\r\n","UTF-8")) connection.sendall(bytes(f":{server} 312 {pending} {target} {server} :{identifier}\r\n","UTF-8"))
if "o" in who_flags: connection.sendall(bytes(f":{server} 313 {pending} {target} :is an IRC operator\r\n","UTF-8")) if "o" in who_flags: connection.sendall(bytes(f":{server} 313 {pending} {target} :is an IRC operator\r\n","UTF-8"))
who_away = property_list[target]["away"] who_away = property_list[target]["away"]
if who_away: if who_away:
who_reason = who_away = property_list[target]["reason"] who_reason = who_away = property_list[target]["reason"]
connection.sendall(bytes(f":{server} 301 {pending} {target} :{who_reason}\r\n","UTF-8")) connection.sendall(bytes(f":{server} 301 {pending} {target} :{who_reason}\r\n","UTF-8"))
if who_identified:
connection.sendall(bytes(f":{server} 330 {pending} {target} {who_identifying} :is logged in as\r\n","UTF-8"))
if who_ssl:
connection.sendall(bytes(f":{server} 671 {pending} {target} :is using a secure connection\r\n","UTF-8"))
#connection.sendall(bytes(f":{server} 317 {pending} {target} {time} :seconds idle\r\n","UTF-8")) # I haven't implemented idle time yet. #connection.sendall(bytes(f":{server} 317 {pending} {target} {time} :seconds idle\r\n","UTF-8")) # I haven't implemented idle time yet.
if who_flags != None and who_flags != "iw": if who_flags != None and who_flags != "iw":
connection.sendall(bytes(f":{server} 379 {pending} {target} :Is using modes +{who_flags}\r\n","UTF-8")) connection.sendall(bytes(f":{server} 379 {pending} {target} :Is using modes +{who_flags}\r\n","UTF-8"))
@ -472,11 +593,11 @@ def session(connection, client, ip, ssl=False):
for i in channels_list[channel]: for i in channels_list[channel]:
try: try:
if i != pending: if i != pending:
nickname_list[i].sendall(bytes(f":{pending}!~{username}@{hostname} {text}\r\n","UTF-8")) nickname_list[i].sendall(bytes(f":{pending}!{rident}@{hostname} {text}\r\n","UTF-8"))
except: except:
pass pass
elif target in nickname_list: elif target in nickname_list:
nickname_list[target].sendall(bytes(f":{pending}!~{username}@{hostname} {text}\r\n","UTF-8")) nickname_list[target].sendall(bytes(f":{pending}!{rident}@{hostname} {text}\r\n","UTF-8"))
else: else:
connection.sendall(bytes(f":{server} 401 {pending} {target} :No such nick/channel\r\n","UTF-8")) connection.sendall(bytes(f":{server} 401 {pending} {target} :No such nick/channel\r\n","UTF-8"))
else: else:
@ -499,7 +620,7 @@ def session(connection, client, ip, ssl=False):
if pending in users: if pending in users:
for j in users: for j in users:
if j != pending and not j in done: if j != pending and not j in done:
nickname_list[j].sendall(bytes(f":{pending}!~{username}@{hostname} {text}\r\n","UTF-8")) nickname_list[j].sendall(bytes(f":{pending}!{rident}@{hostname} {text}\r\n","UTF-8"))
done.append(j) done.append(j)
# Remove the quitting user from the channel. # Remove the quitting user from the channel.
try: try:
@ -508,7 +629,7 @@ def session(connection, client, ip, ssl=False):
print(traceback.format_exc()) print(traceback.format_exc())
# Confirm QUIT and close the socket. # Confirm QUIT and close the socket.
try: try:
connection.sendall(bytes(f":{pending}!~{username}@{hostname} {text}\r\n","UTF-8")) connection.sendall(bytes(f":{pending}!{rident}@{hostname} {text}\r\n","UTF-8"))
connection.sendall(bytes(f"ERROR :Closing Link: {hostname} ({msg})\r\n","UTF-8")) connection.sendall(bytes(f"ERROR :Closing Link: {hostname} ({msg})\r\n","UTF-8"))
finally: finally:
connection.close() connection.close()
@ -541,7 +662,7 @@ def session(connection, client, ip, ssl=False):
else: else:
connection.sendall(bytes(f":{server} 505 {pending} :Cant change mode for other users\r\n","UTF-8")) connection.sendall(bytes(f":{server} 505 {pending} :Cant change mode for other users\r\n","UTF-8"))
elif command == "GITSERV" or (command == "PRIVMSG" and args[0].lower() == "gitserv"): elif command == "CATSERV" or (command == "PRIVMSG" and args[0].lower() == "catserv"):
if command == "PRIVMSG": if command == "PRIVMSG":
args = args[1:] args = args[1:]
if args[0][0] == ":": if args[0][0] == ":":
@ -551,42 +672,22 @@ def session(connection, client, ip, ssl=False):
elif args[0].upper() == "PULL": elif args[0].upper() == "PULL":
updater = subprocess.run(["git", "pull"], stdout=subprocess.PIPE) updater = subprocess.run(["git", "pull"], stdout=subprocess.PIPE)
if updater.stdout.decode().strip() == "Already up to date.": if updater.stdout.decode().strip() == "Already up to date.":
connection.sendall(bytes(f":GitServ!~IRCat@IRCatCore NOTICE {pending} :Codename IRCat is already up-to-date.\r\n","UTF-8")) connection.sendall(bytes(f":CatServ!Meow@IRCatCore NOTICE {pending} :Codename IRCat is already up-to-date.\r\n","UTF-8"))
else: else:
connection.sendall(bytes(f":GitServ!~IRCat@IRCatCore NOTICE {pending} :Done, it is recommended to use /RESTART if you're an IRC op\r\n","UTF-8")) connection.sendall(bytes(f":CatServ!Meow@IRCatCore NOTICE {pending} :Done, it is recommended to use /RESTART if you're an IRC op\r\n","UTF-8"))
elif args[0].upper() == "VERSION": elif args[0].upper() == "VERSION":
connection.sendall(bytes(f":GitServ!~IRCat@IRCatCore NOTICE {pending} :Codename IRCat version {__version__}\r\n","UTF-8")) connection.sendall(bytes(f":CatServ!Meow@IRCatCore NOTICE {pending} :Codename IRCat version {__version__}\r\n","UTF-8"))
connection.sendall(bytes(f":GitServ!~IRCat@IRCatCore NOTICE {pending} :This is Codename IRCat's integrated services.\r\n","UTF-8")) connection.sendall(bytes(f":CatServ!Meow@IRCatCore NOTICE {pending} :This is Codename IRCat's integrated services.\r\n","UTF-8"))
else: else:
connection.sendall(bytes(f":GitServ!~IRCat@IRCatCore NOTICE {pending} :GitServ Usage:\r\n","UTF-8")) connection.sendall(bytes(f":CatServ!Meow@IRCatCore NOTICE {pending} :CatServ Usage:\r\n","UTF-8"))
connection.sendall(bytes(f":GitServ!~IRCat@IRCatCore NOTICE {pending} :PULL - Pulls the latest version of Codename IRCat\r\n","UTF-8")) connection.sendall(bytes(f":CatServ!Meow@IRCatCore NOTICE {pending} :PULL - Pulls the latest version of Codename IRCat\r\n","UTF-8"))
connection.sendall(bytes(f":GitServ!~IRCat@IRCatCore NOTICE {pending} :VERSION - Gets the version number of this service.\r\n","UTF-8")) connection.sendall(bytes(f":CatServ!Meow@IRCatCore NOTICE {pending} :VERSION - Gets the version number of this service.\r\n","UTF-8"))
elif command == "NICKSERV" or (command == "PRIVMSG" and args[0].lower() == "nickserv"):
if command == "PRIVMSG":
args = args[1:]
if args[0][0] == ":":
args[0] = args[0][1:]
if len(args) == 0:
connection.sendall(bytes(f":{server} 461 {pending} {command} :Not enough parameters\r\n","UTF-8"))
elif args[0].upper() == "IDENTIFY":
pass
elif args[0].upper() == "VERSION":
connection.sendall(bytes(f":NickServ!~IRCat@IRCatCore NOTICE {pending} :Codename IRCat version {__version__}\r\n","UTF-8"))
connection.sendall(bytes(f":NickServ!~IRCat@IRCatCore NOTICE {pending} :This is Codename IRCat's integrated services.\r\n","UTF-8"))
else:
connection.sendall(bytes(f":NickServ!~IRCat@IRCatCore NOTICE {pending} :NickServ Usage:\r\n","UTF-8"))
connection.sendall(bytes(f":NickServ!~IRCat@IRCatCore NOTICE {pending} :IDENTIFY - Identifies your nickname\r\n","UTF-8"))
connection.sendall(bytes(f":NickServ!~IRCat@IRCatCore NOTICE {pending} :VERSION - Gets the version number of this service.\r\n","UTF-8"))
elif command == "RESTART": elif command == "RESTART":
if "o" in property_list[pending]["modes"]: if "o" in property_list[pending]["modes"]:
tcp_socket.shutdown(socket.SHUT_RDWR)
tcp_socket.close()
global opened global opened
opened = False opened = False
else: else:
connection.sendall(bytes(f":{server} 481 {pending} :Permission Denied- You're not an IRC operator\r\n","UTF-8")) connection.sendall(bytes(f":{server} 481 {pending} :Permission Denied- You're not an IRC operator\r\n","UTF-8"))
elif command == "PRIVMSG": elif command == "PRIVMSG":
if len(args) >= 2: if len(args) >= 2:
target = text.split(" ")[1] target = text.split(" ")[1]
@ -600,21 +701,18 @@ def session(connection, client, ip, ssl=False):
try: try:
if i != pending: if i != pending:
print(i) print(i)
print(f":{pending}!~{username}@{hostname} {text}\r\n") print(f":{pending}!{rident}@{hostname} {text}\r\n")
nickname_list[i].sendall(bytes(f":{pending}!~{username}@{hostname} {text}\r\n","UTF-8")) nickname_list[i].sendall(bytes(f":{pending}!{rident}@{hostname} {text}\r\n","UTF-8"))
else: else:
print(i + " Is the current user!") print(i + " Is the current user!")
except: except:
print(traceback.format_exc) print(traceback.format_exc)
elif target in nickname_list: elif target in nickname_list:
nickname_list[target].sendall(bytes(f":{pending}!~{username}@{hostname} {text}\r\n","UTF-8")) nickname_list[target].sendall(bytes(f":{pending}!{rident}@{hostname} {text}\r\n","UTF-8"))
else: else:
connection.sendall(bytes(f":{server} 401 {pending} {target} :No such nick/channel\r\n","UTF-8")) connection.sendall(bytes(f":{server} 401 {pending} {target} :No such nick/channel\r\n","UTF-8"))
else: else:
connection.sendall(bytes(f":{server} 461 {pending} {command} :Not enough parameters\r\n","UTF-8")) connection.sendall(bytes(f":{server} 461 {pending} {command} :Not enough parameters\r\n","UTF-8"))
# Ignore empty text # Ignore empty text
elif text.split(" ")[0] == "": elif text.split(" ")[0] == "":
pass pass
@ -622,7 +720,12 @@ def session(connection, client, ip, ssl=False):
# Unknown command # Unknown command
cmd = text.split(" ")[0] cmd = text.split(" ")[0]
connection.sendall(bytes(f":{server} 421 {pending} {cmd} :Unknown command\r\n","UTF-8")) connection.sendall(bytes(f":{server} 421 {pending} {cmd} :Unknown command\r\n","UTF-8"))
pendingCommands = ""
else:
pendingCommands += text
textt = ""
except ssl.SSLEOFError:
print("EOF occured...")
except Exception as ex: except Exception as ex:
print(traceback.format_exc()) print(traceback.format_exc())
cause = "" + str(ex) cause = "" + str(ex)
@ -643,7 +746,7 @@ def session(connection, client, ip, ssl=False):
for j in users: for j in users:
if j != pending and not j in done: if j != pending and not j in done:
try: try:
nickname_list[j].sendall(bytes(f":{pending}!~{username}@{hostname} QUIT :{cause}\r\n","UTF-8")) nickname_list[j].sendall(bytes(f":{pending}!{rident}@{hostname} QUIT :{cause}\r\n","UTF-8"))
done.append(j) done.append(j)
except: except:
print(traceback.format_exc()) print(traceback.format_exc())
@ -670,7 +773,7 @@ def cleanup_manual():
i.remove(h) i.remove(h)
for k in channels_list[j]: for k in channels_list[j]:
if k != h and k in nickname_list: if k != h and k in nickname_list:
nickname_list[k].sendall(f":{h}!~DISCONNECTED@DISCONNECTED PART {j} :IRCat Cleanup: Found missing connection!!\r\n") nickname_list[k].sendall(f":{h}!DISCONNECTED@DISCONNECTED PART {j} :IRCat Cleanup: Found missing connection!!\r\n")
def tcp_session(sock): def tcp_session(sock):
while True: while True:
@ -683,22 +786,28 @@ def tcp_session(sock):
except: except:
print("Something went wrong...") print("Something went wrong...")
print(traceback.format_exc()) print(traceback.format_exc())
def ssl_session(sock2): def ssl_session(sock):
with context.wrap_socket(sock2, server_side=True) as sock:
while True: while True:
try: try:
while opened: while opened:
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.minimum_version = ssl.TLSVersion.TLSv1_2
context.set_ciphers('DEFAULT:@SECLEVEL=0')
context.load_cert_chain(ssl_cert, keyfile=ssl_pkey)
print("Waiting for connection...") print("Waiting for connection...")
connection, client = sock.accept() connection, client = sock.accept()
ip_to = restrict_ip ip_to = restrict_ip
threading.Thread(target=session, daemon=True, args=[connection, client, ip_to]).start() threading.Thread(target=session, daemon=True, args=[context.wrap_socket(connection, server_side=True), client, ip_to, True]).start()
except: except:
print("Something went wrong...") print("Something went wrong...")
print(traceback.format_exc()) print(traceback.format_exc())
for ip, i in sockets.items(): for ip, i in sockets.items():
print("Now listening on port 6667 with IP " + ip) print("Now listening on port 6667 with IP " + ip)
threading.Thread(target=tcp_session, args=[i]).start() threading.Thread(target=tcp_session, args=[i], daemon=True).start()
if ssl_option: if ssl_option:
for ip, i in sockets_ssl.items(): for ip, i in sockets_ssl.items():
print("Now listening on SSL port 6697 with IP " + ip) print("Now listening on SSL port 6697 with IP " + ip)
threading.Thread(target=ssl_session, args=[i]).start() threading.Thread(target=ssl_session, args=[i], daemon=True).start()
while opened:
pass
print("Shutting down...")

30
todo.md
View file

@ -12,12 +12,11 @@
- [x] Send PING and wait for PONG - [x] Send PING and wait for PONG
- [x] Reply PONG if received PING - [x] Reply PONG if received PING
- [x] [Change of nicknames](https://mastodon.swee.codes/@swee/113642104470536887) - [x] [Change of nicknames](https://mastodon.swee.codes/@swee/113642104470536887)
- [ ] Change of hostnames
- [x] Away - [x] Away
- [ ] Multi-server support - [ ] Multi-server support
- [ ] `LIST` - [x] `LIST`
- [ ] `TOPIC` - [ ] `TOPIC`
- [ ] [Data file with SQLite](https://discuss.swee.codes/t/41/2) - [ ] [Database support](https://discuss.swee.codes/t/41)
- [ ] User Flags - [ ] User Flags
- [ ] i (invisible) - [ ] i (invisible)
- [ ] o (IRCOP) - [ ] o (IRCOP)
@ -40,30 +39,29 @@
- [ ] Destructive features for IRCOPS - [ ] Destructive features for IRCOPS
- [ ] `KILL <user> <comment>` - [ ] `KILL <user> <comment>`
- [ ] `MODE <external user>` - [ ] `MODE <external user>`
- [ ] `RESTART` - [x] `RESTART`
- [ ] Extra commands - [ ] Extra commands
- [ ] `USERS` - [x] `NAMES`
- [x] `WHOIS` - [x] `WHOIS`
- [ ] `WHOWAS` - [ ] `WHOWAS`
- [ ] Implement services. - [ ] [Implement services.](modules/pawserv.py)
- [ ] Nickserv - [ ] Nickserv
- [ ] ChanServ - [ ] ChanServ
- [x] GitServ (Custom user for pull) - [x] CatServ (Outside of PawServ)
- [ ] Link `PRIVMSG *serv` to `*serv` - [x] Link `PRIVMSG *serv` to `*serv`
- [ ] Extra (not planned) features - [x] Extra ~~(not planned)~~ features
- [ ] ident support - [x] ident support
- [ ] Authentication - [ ] Authentication
- [ ] Make the server able to change the client's host - [x] Store credentials in an SQLite3 file.
- [ ] Store credentials in an SQLite3 file. - [x] Map NickServ IDENTIFY
- [ ] Map NickServ IDENTIFY
- [ ] Map PASS - [ ] Map PASS
- [ ] Mock SASL PLAIN - [x] SSL/TLS
- [ ] SSL/TLS
- [x] [Use a thread to accept connections on SSL port 6697](https://mastodon.swee.codes/@swee/113762525145710774) - [x] [Use a thread to accept connections on SSL port 6697](https://mastodon.swee.codes/@swee/113762525145710774)
- [ ] Automatically reload the certificate if defined in config. - [x] Automatically reload the certificate ~~if defined in config.~~
- [ ] Add IRCv3 features. - [ ] Add IRCv3 features.
- [x] List capabilities (`CAP LS 302`) - [x] List capabilities (`CAP LS 302`)
- [ ] `away-notify` - [ ] `away-notify`
- [ ] `tls` (STARTTLS) - [ ] `tls` (STARTTLS)
- [ ] `sasl`
- Will research later. - Will research later.
I am going to fully read [RFC 1459](https://datatracker.ietf.org/doc/html/rfc1459) soon and add each part to the TODO. I am going to fully read [RFC 1459](https://datatracker.ietf.org/doc/html/rfc1459) soon and add each part to the TODO.