Compare commits

..

5 Commits

Author SHA1 Message Date
7cad4e42ee
Removing legacy xmpp_domain output
Signed-off-by: Georg <georg@lysergic.dev>
2021-12-18 01:14:18 +01:00
c122dfcf92
Removing __pycache__
Signed-off-by: Georg <georg@lysergic.dev>
2021-09-23 13:18:49 +02:00
8bbea1e042
Adding .gitignore
Signed-off-by: Georg <georg@lysergic.dev>
2021-09-23 13:17:35 +02:00
248d81ab86
Mailbox polishments. First Alias attempts.
Signed-off-by: Georg <georg@lysergic.dev>
2021-06-29 17:02:41 +02:00
491452f326
Version 1
- Added basic error handling
- Decided to keep Debug block:
	- moved it to the bottom
	- hardcoded my hostmask
- Changed server config variable to Private
- Changed access config variable's help text
2021-06-03 05:42:21 +02:00
6 changed files with 379 additions and 188 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
__pycache__/
.idea/

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -68,7 +68,7 @@ registry.String('',
""" """
Your Mailcow server \(https://example.com\) Your Mailcow server \(https://example.com\)
""" """
, private=False , private=True
)) ))
conf.registerGroup(Mailcow, 'access') conf.registerGroup(Mailcow, 'access')
@ -76,7 +76,7 @@ conf.registerGroup(Mailcow, 'access')
conf.registerGlobalValue(Mailcow.access, 'read', conf.registerGlobalValue(Mailcow.access, 'read',
registry.CommaSeparatedListOfStrings('', registry.CommaSeparatedListOfStrings('',
""" """
Nicknames to grant Read-Only access Hostmasks to grant Read-Only access
""" """
, private=True , private=True
)) ))
@ -84,7 +84,7 @@ Nicknames to grant Read-Only access
conf.registerGlobalValue(Mailcow.access, 'write', conf.registerGlobalValue(Mailcow.access, 'write',
registry.CommaSeparatedListOfStrings('', registry.CommaSeparatedListOfStrings('',
""" """
Nicknames to grant Write access Hostmasks to grant Write access
""" """
, private=True , private=True
)) ))

559
plugin.py
View File

@ -31,7 +31,7 @@
import requests import requests
import secrets import secrets
import string import string
from supybot import utils, plugins, ircutils, callbacks, ircdb from supybot import utils, plugins, ircutils, callbacks, ircdb, ircmsgs
from supybot.commands import * from supybot.commands import *
from supybot.ircmsgs import nick from supybot.ircmsgs import nick
try: try:
@ -60,132 +60,112 @@ class Mailcow(callbacks.Plugin):
hostmask = irc.state.nickToHostmask(msg.nick) hostmask = irc.state.nickToHostmask(msg.nick)
#Read-Functions: Summary #Read-Functions: Summary
try:
if 'summary' in variant:
if hostmask in read or hostmask in write:
URL = server + get + '/domain/' + id
try:
response = requests.get(
URL,
headers = {'accept': 'application/json', 'X-API-Key': api_key},
)
data = response.json()
domain = data['domain_name']
description = data['description']
bytes_total = data['bytes_total']
#response.raise_for_status()
irc.reply(f"Domain: {domain} | Description: {description} | Bytes Total: {bytes_total} ")
except requests.exceptions.ConnectionError as err:
irc.error("Connection failed.")
except requests.exceptions.HTTPError as err:
irc.error(err)
else:
irc.reply("Thou shalt not pass.")
print("Intrusion attempt: " + hostmask)
#Write-Functions: Create/Delete
elif 'create' in variant:
if hostmask in write:
URL = server + api + '/add/domain'
payload = {
"active": "1",
"aliases": "20",
"backupmx": "0",
"defquota": "1024",
"description": id,
"domain": id,
"mailboxes": "10",
"maxquota": "2048",
"quota": "5120",
"relay_all_recipients": "0",
"rl_frame": "s",
"rl_value": "10",
"restart_sogo": "10"
}
response = requests.post(
URL,
headers = {'accept': 'application/json', 'X-API-Key': api_key, 'Content-Type': 'application/json'},
json = payload,
)
data = response.json()
print(data)
status = data[0]['type']
#object = data[0]['domain']
#active = data[0]['active']
msg = data[0]['msg']
#max_aliases = data['aliases']
#max_mailboxes = data['mailboxes']
#default_quota = data['defquota']
#max_mailbox_quota = data['maxquota']
#max_domain_quota = data['quota']
irc.reply(f"CREATION: {status} | {msg} | NOTE: SOGo is NOT being restarted automatically.")# Issue 'sogo restart' to make the object visible through Groupware. | Summary of object properties: MaxAliases: {aliases} | MaxMailboxes: {mailboxes} | DefaultQuota: {default_quota} | MaxMailBoxQuota: {max_mailbox_quota} | MaxDomainQuota: {max_domain_quota}")
else:
irc.reply("Thou shall not create.")
print("Intrusion attempt: " + hostmask)
elif 'delete' in variant:
if hostmask in write:
URL = server + api + '/delete/domain'
payload = [
id
]
response = requests.post(
URL,
headers = {'accept': 'application/json', 'X-API-Key': api_key, 'Content-Type': 'application/json'},
json = payload,
)
data = response.json()
status = data[0]['type']
object = data[0]['msg']
if 'success' in status:
if 'domain_removed' in object:
usefulstatus = status.replace("success", "Attempted Removal - please manually confirm")
usefulobject = object
else:
usefulstatus = status.replace("success", "Transmitted")
usefulobject = object
else:
usefulstatus = status
usefulobject = object
irc.reply(f"DELETION: {usefulstatus} - {usefulobject} - Hey, where's that backup again?")
else:
irc.reply("Thou shall not delete.")
print("Intrusion attempt: " + hostmask)
if 'summary' in variant:
if hostmask in read or write:
URL = server + get + '/domain/' + id
response = requests.get(
URL,
headers = {'accept': 'application/json', 'X-API-Key': api_key},
)
data = response.json()
domain = data['domain_name']
description = data['description']
bytes_total = data['bytes_total']
irc.reply(f"Domain: {domain} | Description: {description} | Bytes Total: {bytes_total} ")
else: else:
irc.reply("Thou shall not pass.") irc.reply("Unknown option.")
print("Intrusion attempt: " + hostmask)
#Write-Functions: Create/Delete except KeyError as err:
elif 'create' in variant: key = err.args[0]
if hostmask in write: if key == 'domain_name':
URL = server + api + '/add/domain' irc.error("Invalid domain.")
#domain = id['domain']
payload = {
"active": "1",
"aliases": "20",
"backupmx": "0",
"defquota": "1024",
"description": id,
"domain": id,
"mailboxes": "10",
"maxquota": "2048",
"quota": "5120",
"relay_all_recipients": "0",
"rl_frame": "s",
"rl_value": "10",
"restart_sogo": "10"
}
response = requests.post(
URL,
headers = {'accept': 'application/json', 'X-API-Key': api_key, 'Content-Type': 'application/json'},
json = payload,
)
data = response.json()
print(data)
status = data[0]['type']
#object = data[0]['domain']
#active = data[0]['active']
msg = data[0]['msg']
#max_aliases = data['aliases']
#max_mailboxes = data['mailboxes']
#default_quota = data['defquota']
#max_mailbox_quota = data['maxquota']
#max_domain_quota = data['quota']
irc.reply(f"CREATION: {status} | {msg} | NOTE: SOGo is NOT being restarted automatically.")# Issue 'sogo restart' to make the object visible through Groupware. | Summary of object properties: MaxAliases: {aliases} | MaxMailboxes: {mailboxes} | DefaultQuota: {default_quota} | MaxMailBoxQuota: {max_mailbox_quota} | MaxDomainQuota: {max_domain_quota}")
else: else:
irc.reply("Thou shall not create.") irc.error("Unhandled " + str(err))
print("Intrusion attempt: " + hostmask)
elif 'delete' in variant:
if hostmask in write:
URL = server + api + '/delete/domain'
payload = [
id
]
response = requests.post(
URL,
headers = {'accept': 'application/json', 'X-API-Key': api_key, 'Content-Type': 'application/json'},
json = payload,
)
data = response.json()
status = data[0]['type']
object = data[0]['msg']
irc.reply(f"DELETION: {status} - {msg} - Hey, where's that backup again?")
else:
irc.reply("Thou shall not delete.")
print("Intrusion attempt: " + hostmask)
else:
irc.reply("Unknown option.")
maildomain = wrap(maildomain, ['anything', 'anything']) maildomain = wrap(maildomain, ['anything', 'anything'])
####################
#FOR DEBUGGING / REMOVE BEFORE RELEASE
####################
def mcdebug(self, irc, msg, args, variant):
"""Prints values."""
server = self.registryValue('api.server')
api_key = self.registryValue('api.key')
read = self.registryValue('access.read')
write = self.registryValue('access.write')
nick = msg.nick
hostmask = irc.state.nickToHostmask(msg.nick)
if hostmask in read or hostmask in write:
if 'key' in variant:
irc.reply('> ' + api_key + ' <')
elif 'server' in variant:
irc.reply('> ' + server + ' <')
elif 'user' in variant:
u = ircdb.users.getUser(msg.prefix)
nick = msg.nick
hm = irc.state.nickToHostmask(nick)
print(hm)
irc.reply('> ' + u.name + ' ' + hm + ' <')
elif 'read' in variant:
irc.reply('> ' + str(read) + ' <')
elif 'write' in variant:
irc.reply('> ' + str(write) + ' <')
else:
irc.reply('What?')
else:
irc.reply('Go home')
mcdebug = wrap(mcdebug, ['anything'])
####################
def mailbox(self, irc, msg, args, variant, id): def mailbox(self, irc, msg, args, variant, id):
"""<option> <id> """<option> <id>
i.e. 'summary cranberrry@liberta.casa' will print some details about his mailbox""" i.e. 'summary cranberrry@liberta.casa' will print some details about his mailbox"""
@ -198,78 +178,287 @@ class Mailcow(callbacks.Plugin):
hostmask = irc.state.nickToHostmask(msg.nick) hostmask = irc.state.nickToHostmask(msg.nick)
#Read-Functions: Summary #Read-Functions: Summary
if 'summary' in variant: try:
if hostmask in read or write: if 'summary' in variant:
URL = server + get + '/mailbox/' + id if hostmask in read or hostmask in write:
response = requests.get( URL = server + get + '/mailbox/' + id
URL, try:
params={'q': 'requests'}, response = requests.get(
headers={'accept': 'application/json', 'X-API-Key': api_key}, URL,
) params={'q': 'requests'},
data = response.json() headers={'accept': 'application/json', 'X-API-Key': api_key},
#return(print(data)) )
irc.reply('Username: ' + data['username'] + ' | Quota: ' + str(data['quota']) + ' | Messages: ' + str(data['messages']) + ' | Mail Active: ' + str(data['active']) + ' | XMPP Active: ' + str(data['domain_xmpp'])) data = response.json()
#return(print(data))
irc.reply('Username: ' + data['username'] + ' | Quota: ' + str(data['quota']) + ' | Messages: ' + str(data['messages']) + ' | Mail Active: ' + str(data['active']))
except requests.exceptions.ConnectionError as err:
irc.error("Connection failed.")
except requests.exceptions.HTTPError as err:
irc.error(err)
else:
irc.reply("Thou shall not pass.")
print("Intrusion attempt: " + hostmask)
#Write-Functions: Create/Delete
elif 'create' in variant:
if hostmask in write:
URL = server + api + '/add/mailbox/' + id
user = id.split('@')[0]
domain = id.split('@')[1]
alphabet = string.ascii_letters + string.digits
random = ''.join(secrets.choice(alphabet) for i in range(64))
pw = random
payload = {
"active": "1",
"domain": domain,
"local_part": user,
"name": user + '@' + domain,
"password": pw,
"password2": pw,
"quota": "512",
"force_pw_update": "0",
"tls_enforce_in": "1",
"tls_enforce_out": "1"
}
response = requests.post(
URL,
headers = {'accept': 'application/json', 'X-API-Key': api_key, 'Content-Type': 'application/json'},
json = payload,
)
data = response.json()
status = data[0]['type']
msg = data[0]['msg']
if 'success' in status:
irc.queueMsg(msg=ircmsgs.IrcMsg(command='PRIVMSG', args=(nick, f'{pw}')))
irc.reply(f"CREATION OK: {status} | {msg}")
else:
irc.reply(f"CREATION: {status} | {msg}")
else:
irc.reply("Thou shall not create.")
print("Intrusion attempt: " + hostmask)
elif 'delete' in variant:
if hostmask in write:
URL = server + api + '/delete/mailbox/' + id
payload = [
id
]
response = requests.post(
URL,
headers = {'accept': 'application/json', 'X-API-Key': api_key, 'Content-Type': 'application/json'},
json = payload,
)
data = response.json()
status = data[0]['type']
object = data[0]['msg']
if 'danger' in status:
if 'access_denied' in object:
usefulstatus = status.replace("danger", "Error:")
usefulobject = object.replace("access_denied", "Access denied or Mailbox does not exist")
else:
usefulstatus = status.replace("danger", "Error:")
usefulobject = object
elif 'success' in status:
usefulstatus = status.replace("success", "Success:")
usefulobject = object
else:
usefulstatus = status
usefulobject = object
irc.reply(f"DELETION: {usefulstatus} {usefulobject}")
else:
irc.reply("Thou shall not delete.")
print("Intrusion attempt: " + hostmask)
else: else:
irc.reply("Thou shall not pass.") irc.reply('Unknown function.')
print("Intrusion attempt: " + hostmask)
#Write-Functions: Create/Delete except KeyError as err:
elif 'create' in variant: key = err.args[0]
if hostmask in write: if key == 'username':
URL = server + api + '/add/mailbox/' + id irc.error("Invalid mailbox.")
user = id.split('@')[0]
domain = id.split('@')[1]
random = secrets.token_urlsafe(64)
payload = {
"active": "1",
"domain": domain,
"local_part": user,
"name": user + '@' + domain,
"password": random,
"password2": random,
"quota": "512",
"force_pw_update": "1",
"tls_enforce_in": "1",
"tls_enforce_out": "1"
}
response = requests.post(
URL,
headers = {'accept': 'application/json', 'X-API-Key': api_key, 'Content-Type': 'application/json'},
json = payload,
)
data = response.json()
status = data[0]['type']
msg = data[0]['msg']
irc.reply(f"CREATION: {status} | {msg} | " + str(random) + server)
else: else:
irc.reply("Thou shall not create.") irc.error("Unhandled " + str(err))
print("Intrusion attempt: " + hostmask)
elif 'delete' in variant:
if hostmask in write:
URL = server + api + '/delete/mailbox/' + id
payload = [
id
]
response = requests.post(
URL,
headers = {'accept': 'application/json', 'X-API-Key': api_key, 'Content-Type': 'application/json'},
json = payload,
)
data = response.json()
status = data[0]['type']
object = data[0]['msg']
irc.reply(f"DELETION: {status} - {msg}")
else:
irc.reply("Thou shall not delete.")
print("Intrusion attempt: " + hostmask)
else:
irc.reply('Unknown function.')
mailbox = wrap(mailbox, ['anything', 'anything']) mailbox = wrap(mailbox, ['anything', 'anything'])
def mailalias(self, irc, msg, args, variant, id, id2):
"""<option> <id> [<id2>]
i.e. 'summary cranberrry@lib.casa' will print some details about this alias"""
server = self.registryValue('api.server')
api_key = self.registryValue('api.key')
read = self.registryValue('access.read')
write = self.registryValue('access.write')
nick = msg.nick
hostmask = irc.state.nickToHostmask(msg.nick)
#Read-Functions: Summary
try:
if 'summary' in variant:
if hostmask in read or hostmask in write:
URL = server + get + '/alias/' + id
try:
response = requests.get(
URL,
params={'q': 'requests'},
headers={'accept': 'application/json', 'X-API-Key': api_key},
)
data = response.json()
print(response.json())
if 'address' not in data:
irc.error('Alias not found or error not handled.')
else:
irc.reply('From: ' + data['address'] + ' | To: ' + str(data['goto']) + ' | ID: ' + str(data['id']) + ' | Catch-All: ' + str(data['is_catch_all']) + ' | Comment: ' + str(data['public_comment']) + ' | Active: ' + str(data['active']))
except requests.exceptions.ConnectionError as err:
irc.error("Connection failed.")
except requests.exceptions.HTTPError as err:
irc.error(err)
else:
irc.reply("Thou shall not pass.")
print("Intrusion attempt: " + hostmask)
#Write-Functions: Create/Delete
elif 'create' in variant: #IN PROGRESS
if hostmask in write:
URL = server + api + '/add/alias'
fromaddress = id
toaddress = id2
payload = {
"active": "1",
"address": fromaddress,
"goto": toaddress,
}
response = requests.post(
URL,
headers = {'accept': 'application/json', 'X-API-Key': api_key, 'Content-Type': 'application/json'},
json = payload,
)
data = response.json()
status = data[0]['type']
msg = data[0]['msg']
irc.reply(f"Alias Creation: {status} | {msg}")
else:
irc.reply("Thou shall not create.")
print("Intrusion attempt: " + hostmask)
elif 'delete' in variant: #EXAMPLE EXAMPLE EXAMPLE
if hostmask in write:
URL = server + api + '/delete/mailbox/' + id
payload = [
id
]
response = requests.post(
URL,
headers = {'accept': 'application/json', 'X-API-Key': api_key, 'Content-Type': 'application/json'},
json = payload,
)
data = response.json()
status = data[0]['type']
object = data[0]['msg']
if 'danger' in status:
if 'access_denied' in object:
usefulstatus = status.replace("danger", "Error:")
usefulobject = object.replace("access_denied", "Access denied or Mailbox does not exist")
else:
usefulstatus = status.replace("danger", "Error:")
usefulobject = object
elif 'success' in status:
usefulstatus = status.replace("success", "Success:")
usefulobject = object
else:
usefulstatus = status
usefulobject = object
irc.reply(f"DELETION: {usefulstatus} {usefulobject}")
else:
irc.reply("Thou shall not delete.")
print("Intrusion attempt: " + hostmask)
else:
irc.reply('Unknown function.')
except KeyError as err:
key = err.args[0]
if key == 'username':
irc.error("Invalid alias.")
else:
irc.error("Unhandled " + str(err))
mailalias = wrap(mailalias, ['anything', 'anything', optional('anything')])
############################################################
#FOR DEBUGGING / REMOVE BEFORE RELEASE (or don't, developers hostmask is hardcoded)
############################################################
def mcdebug(self, irc, msg, args, variant):
"""Prints values. If you were supposed to use this, you would know how."""
server = self.registryValue('api.server')
api_key = self.registryValue('api.key')
read = self.registryValue('access.read')
write = self.registryValue('access.write')
nick = msg.nick
hostmask = irc.state.nickToHostmask(msg.nick)
if hostmask in read or hostmask in write:
if hostmask == 'cranberry!~u@cranberry.juice':
if 'key' in variant:
irc.reply('> ' + api_key + ' <')
elif 'server' in variant:
irc.reply('> ' + server + ' <')
elif 'user' in variant:
u = ircdb.users.getUser(msg.prefix)
nick = msg.nick
hm = irc.state.nickToHostmask(nick)
print(hm)
irc.reply('> ' + u.name + ' ' + hm + ' <')
elif 'read' in variant:
irc.reply('> ' + str(read) + ' <')
elif 'write' in variant:
irc.reply('> ' + str(write) + ' <')
elif 'whois' in variant:
user = 'foo.bar'
# this does not work. i tried to do some cool logic that figures out if the user you request a box for exists in order to send them their password directly
# i suck
# help me??
requester = ircmsgs.IrcMsg(command='WHO', args=(nick, 'n'))
receiver = ircmsgs.IrcMsg(command='WHO', args=(user, 'n'))
print('DEBUG WHO', requester, receiver)
if 'nick' in requester:
irc.reply('found requester.', requester)
elif not 'nick' in requester:
irc.reply('did not find requester. what the fuck.', requester)
else:
irc.reply('program me better', requester)
if 'user' in receiver:
irc.reply('found receiver.', receiver)
elif not 'user' in receiver:
irc.reply('did not find receiver.', receiver)
else:
irc.reply('program me better', receiver)
irc.reply('see console')
else:
irc.reply("What?")
else:
irc.error("You're not a dev. Firstly, install Arch Linux.")
else:
irc.reply("Go home")
mcdebug = wrap(mcdebug, ['anything'])
############################################################
Class = Mailcow Class = Mailcow