2021-05-22 03:13:53 +02:00
###
# Copyright (c) 2021, Georg Pfuetzenreuter
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
import requests
import secrets
import string
2021-06-29 17:02:41 +02:00
from supybot import utils , plugins , ircutils , callbacks , ircdb , ircmsgs
2021-05-22 03:13:53 +02:00
from supybot . commands import *
2021-06-03 01:46:17 +02:00
from supybot . ircmsgs import nick
2021-05-22 03:13:53 +02:00
try :
from supybot . i18n import PluginInternationalization
_ = PluginInternationalization ( ' Mailcow ' )
except ImportError :
# Placeholder that allows to run the plugin on a bot
# without the i18n module
_ = lambda x : x
api = ' /api/v1 '
get = api + ' /get '
class Mailcow ( callbacks . Plugin ) :
""" Mailcow API through IRC """
pass
2021-06-03 01:46:17 +02:00
2021-05-22 03:13:53 +02:00
def maildomain ( self , irc , msg , args , variant , id ) :
2021-06-03 01:46:17 +02:00
""" <option> <id>
i . e . ' summary liberta.casa ' will print infos about the respective zone """
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
2021-06-03 05:42:21 +02:00
try :
2021-06-29 17:02:41 +02:00
if ' summary ' in variant :
if hostmask in read or hostmask in write :
2021-06-03 05:42:21 +02:00
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 :
2021-06-29 17:02:41 +02:00
irc . error ( err )
2021-06-03 05:42:21 +02:00
else :
irc . reply ( " Thou shalt not pass. " )
print ( " Intrusion attempt: " + hostmask )
2021-05-22 03:13:53 +02:00
2021-06-03 05:42:21 +02:00
#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 )
2021-06-29 17:02:41 +02:00
2021-06-03 05:42:21 +02:00
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 ' ]
2021-06-29 17:02:41 +02:00
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? " )
2021-06-03 05:42:21 +02:00
else :
irc . reply ( " Thou shall not delete. " )
print ( " Intrusion attempt: " + hostmask )
2021-05-22 03:13:53 +02:00
2021-06-03 01:46:17 +02:00
else :
2021-06-03 05:42:21 +02:00
irc . reply ( " Unknown option. " )
2021-05-22 03:13:53 +02:00
2021-06-03 05:42:21 +02:00
except KeyError as err :
key = err . args [ 0 ]
if key == ' domain_name ' :
irc . error ( " Invalid domain. " )
2021-06-03 01:46:17 +02:00
else :
2021-06-03 05:42:21 +02:00
irc . error ( " Unhandled " + str ( err ) )
2021-06-29 17:02:41 +02:00
2021-05-22 03:13:53 +02:00
maildomain = wrap ( maildomain , [ ' anything ' , ' anything ' ] )
2021-06-03 05:42:21 +02:00
def mailbox ( self , irc , msg , args , variant , id ) :
""" <option> <id>
i . e . ' summary cranberrry@liberta.casa ' will print some details about his mailbox """
2021-06-03 01:46:17 +02:00
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 )
2021-06-03 05:42:21 +02:00
#Read-Functions: Summary
try :
if ' summary ' in variant :
2021-06-29 17:02:41 +02:00
if hostmask in read or hostmask in write :
2021-06-03 05:42:21 +02:00
URL = server + get + ' /mailbox/ ' + id
try :
response = requests . get (
URL ,
params = { ' q ' : ' requests ' } ,
headers = { ' accept ' : ' application/json ' , ' X-API-Key ' : api_key } ,
)
data = response . json ( )
#return(print(data))
2021-12-18 01:14:18 +01:00
irc . reply ( ' Username: ' + data [ ' username ' ] + ' | Quota: ' + str ( data [ ' quota ' ] ) + ' | Messages: ' + str ( data [ ' messages ' ] ) + ' | Mail Active: ' + str ( data [ ' active ' ] ) )
2021-06-03 05:42:21 +02:00
except requests . exceptions . ConnectionError as err :
irc . error ( " Connection failed. " )
except requests . exceptions . HTTPError as err :
2021-06-29 17:02:41 +02:00
irc . error ( err )
2021-06-03 05:42:21 +02:00
else :
irc . reply ( " Thou shall not pass. " )
print ( " Intrusion attempt: " + hostmask )
2021-06-03 01:46:17 +02:00
2021-06-03 05:42:21 +02:00
#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 ]
2021-06-29 17:02:41 +02:00
alphabet = string . ascii_letters + string . digits
random = ' ' . join ( secrets . choice ( alphabet ) for i in range ( 64 ) )
pw = random
2021-06-03 05:42:21 +02:00
payload = {
" active " : " 1 " ,
" domain " : domain ,
" local_part " : user ,
" name " : user + ' @ ' + domain ,
2021-06-29 17:02:41 +02:00
" password " : pw ,
" password2 " : pw ,
2021-06-03 05:42:21 +02:00
" quota " : " 512 " ,
2021-06-29 17:02:41 +02:00
" force_pw_update " : " 0 " ,
2021-06-03 05:42:21 +02:00
" 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 ' ]
2021-06-03 01:46:17 +02:00
2021-06-29 17:02:41 +02:00
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 } " )
2021-06-03 05:42:21 +02:00
else :
irc . reply ( " Thou shall not create. " )
print ( " Intrusion attempt: " + hostmask )
2021-06-03 01:46:17 +02:00
2021-06-03 05:42:21 +02:00
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 ' ]
2021-06-29 17:02:41 +02:00
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
2021-06-03 01:46:17 +02:00
2021-06-29 17:02:41 +02:00
irc . reply ( f " DELETION: { usefulstatus } { usefulobject } " )
2021-06-03 05:42:21 +02:00
else :
irc . reply ( " Thou shall not delete. " )
print ( " Intrusion attempt: " + hostmask )
2021-06-29 17:02:41 +02:00
2021-06-03 05:42:21 +02:00
else :
irc . reply ( ' Unknown function. ' )
2021-06-03 01:46:17 +02:00
2021-06-03 05:42:21 +02:00
except KeyError as err :
key = err . args [ 0 ]
if key == ' username ' :
irc . error ( " Invalid mailbox. " )
2021-06-03 01:46:17 +02:00
else :
2021-06-03 05:42:21 +02:00
irc . error ( " Unhandled " + str ( err ) )
2021-06-03 01:46:17 +02:00
2021-06-03 05:42:21 +02:00
mailbox = wrap ( mailbox , [ ' anything ' , ' anything ' ] )
2021-06-03 01:46:17 +02:00
2021-06-29 17:02:41 +02:00
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 ' ) ] )
2021-06-03 05:42:21 +02:00
############################################################
#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. """
2021-05-22 03:13:53 +02:00
2021-06-03 01:46:17 +02:00
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 )
2021-05-22 03:13:53 +02:00
2021-06-03 05:42:21 +02:00
if hostmask in read or hostmask in write :
if hostmask == ' cranberry!~u@cranberry.juice ' :
2021-06-29 17:02:41 +02:00
if ' key ' in variant :
2021-06-03 05:42:21 +02:00
irc . reply ( ' > ' + api_key + ' < ' )
2021-06-29 17:02:41 +02:00
elif ' server ' in variant :
2021-06-03 05:42:21 +02:00
irc . reply ( ' > ' + server + ' < ' )
2021-06-29 17:02:41 +02:00
2021-06-03 05:42:21 +02:00
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 + ' < ' )
2021-06-03 01:46:17 +02:00
2021-06-03 05:42:21 +02:00
elif ' read ' in variant :
irc . reply ( ' > ' + str ( read ) + ' < ' )
2021-05-22 03:13:53 +02:00
2021-06-03 05:42:21 +02:00
elif ' write ' in variant :
irc . reply ( ' > ' + str ( write ) + ' < ' )
2021-05-22 03:13:53 +02:00
2021-06-29 17:02:41 +02:00
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 ' )
2021-06-03 05:42:21 +02:00
else :
irc . reply ( " What? " )
2021-06-03 01:46:17 +02:00
else :
2021-06-03 05:42:21 +02:00
irc . error ( " You ' re not a dev. Firstly, install Arch Linux. " )
2021-05-22 03:13:53 +02:00
else :
2021-06-03 05:42:21 +02:00
irc . reply ( " Go home " )
2021-05-22 03:13:53 +02:00
2021-06-03 05:42:21 +02:00
mcdebug = wrap ( mcdebug , [ ' anything ' ] )
############################################################
2021-05-22 03:13:53 +02:00
Class = Mailcow
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: