diff --git a/reaction-mailcreate/createMails.py b/reaction-mailcreate/createMails.py
index 189a828833ee25e5ff2948589f942b0e60e33951..93a73c38becb43113f41fc1fbc86580338912334 100755
--- a/reaction-mailcreate/createMails.py
+++ b/reaction-mailcreate/createMails.py
@@ -18,6 +18,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 
+from email.generator import Generator
 from email.message import EmailMessage
 from email.utils import formatdate
 import argparse
@@ -27,6 +28,7 @@ import getpass
 import hashlib
 import jinja2
 import os
+import magic
 import requests
 import secrets
 import smtplib
@@ -43,17 +45,19 @@ if sys.version_info < (3, 8):
     parser.register('action', 'extend', ExtendAction)
 
 # Parse args
+parser.add_argument('-a', '--attach',     dest='attach',     default=[], nargs=1, action='extend', help='add attachment(s) to mail (default: None)')
 parser.add_argument('-b', '--basedir',    dest='basedir',    default='Mails', help='base directory for all output (default: "Mails")')
-parser.add_argument('-B', '--bcc',        dest='bcc',        default=[], nargs=1, action='extend', help='additional mail recipient to bcc (default: None)')
+parser.add_argument('-B', '--bcc',        dest='bcc',        default=[], nargs=1, action='extend', help='additional mail recipient(s) to bcc (default: None)')
 parser.add_argument('-c', '--campaign',   dest='campaign',   default='Test', help='campaign name (default: "Test")')
+parser.add_argument('-C', '--cc',         dest='cc',         default=[], nargs=1, action='extend', help='additional mail recipient(s) to cc (default: None)')
 parser.add_argument('-d', '--dry-run',    dest='dryrun',     default=False, action='store_true', help='dry run -- do not actually send mails or create targets (default: False)')
 parser.add_argument('-f', '--from',       dest='sender',     default='Nobody <nobody@example.com>', help='sender mail address (default: "Nobody <nobody@example.com>"; implies dry-run if not set)')
 parser.add_argument('-F', '--force',      dest='force',      default=False, action='store_true', help='force insecure login without TLS/SSL (default: False)')
 parser.add_argument('-H', '--hashstring', dest='hashstring', default='{salt}{campaign}{infix}-{site}', help='string to be hashed for the URL (default: "{salt}{campaign}{infix}-{site}" where "{salt}" is a random string)')
 parser.add_argument('-i', '--input',      dest='input',      default='{basedir}/{campaign}/Input{infix}.lst', help='input file (default: "{basedir}/{campaign}/Input{infix}.lst")')
 parser.add_argument('-o', '--output',     dest='output',     default='{basedir}/{campaign}/{site}/Mail{infix}.txt', help='output file name template (default: "{basedir}/{campaign}/{site}/Mail{infix}.txt")')
-parser.add_argument(      '--salt',       dest='salt',       default=None, help='salt to use for hashing (default: random 8-byte hex string)')
 parser.add_argument('-R', '--reply-to',   dest='replyto',    default=None, help='reply-to mail address (default: None)')
+parser.add_argument(      '--salt',       dest='salt',       default=None, help='salt to use for hashing (default: random 8-byte hex string)')
 parser.add_argument('-s', '--subject',    dest='subject',    default='Security Challenge for {site} -- {campaign}{infix}', help='mail subject (default: "Security Challenge Message -- {campaign}{infix}")')
 parser.add_argument('-S', '--smtpserver', dest='smtpserver', default='localhost', help='SMTP server to use (default: "localhost"); port can be specified with "<host>:<port>" notation and takes precedence over implied ports and port specification')
 parser.add_argument(      '--smtpport',   dest='smtpport',   default=0, type=int, help='SMTP port to use (default: 25); takes precedence over implied ports')
@@ -63,8 +67,9 @@ parser.add_argument(      '--starttls',   dest='starttls',   default=False, acti
 parser.add_argument('-t', '--template',   dest='template',   default='{basedir}/{campaign}/Mail.template', help='mail template file (default: "{basedir}/{campaign}/Mail.template")')
 parser.add_argument('-T', '--to',         dest='to',         default='{firstname} {lastname} <{email}>', help='recipient mail address (default: "{firstname} {lastname} <{email}>")')
 parser.add_argument('-u', '--url',        dest='url',        default='{webserver}/{campaign}{infix}-{hash}', help='URL template to use (default: "{webserver}/{campaign}{infix}-{hash}"')
+parser.add_argument('-U', '--createurl',  dest='createurl',  default='{webserver}/{campaign}{infix}-{hash}/create', help='URL template to use for creation URL (default: "{webserver}/{campaign}{infix}-{hash}/create"')
 parser.add_argument('-v', '--verbose',    dest='verbose',    default=False, action='store_true', help='increase verbosity')
-parser.add_argument('-w', '--webserver',  dest='webserver',  default='https://challenge.example.com', help='web server to use (default: "https://challenge.example.com"; implies dry-run if not set')
+parser.add_argument('-w', '--webserver',  dest='webserver',  default='https://challenge.example.com', help='web server to use (default: "https://challenge.example.com"; implies dry-run if not set; to suppress web-hook calling, set to empty string ""')
 parser.add_argument('infix',                                 default='', nargs='?', help='infix for ID purposes, default empty')
 args = parser.parse_args()
 
@@ -110,10 +115,14 @@ if not args.salt:
     args.salt = secrets.token_hex(8)
 
 if args.verbose:
+    if args.attach:
+        print(f'Using "{", ".join(args.attach)}" as mail attachment(s).')
     print(f'Using "{args.basedir}" as base directory.')
     if args.bcc:
-        print(f'Using "{", ".join(args.bcc)}" as silent mail recipients.')
+        print(f'Using "{", ".join(args.bcc)}" as bcc mail recipient(s).')
     print(f'Using "{args.campaign}" as campaign.')
+    if args.cc:
+        print(f'Using "{", ".join(args.cc)}" as cc mail recipient(s).')
     print(f'Using "{args.sender}" as sender mail address.')
     if args.replyto:
         print(f'Using "{args.replyto}" as reply-to mail address.')
@@ -125,6 +134,7 @@ if args.verbose:
     print(f'Using "{args.template.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt))}" as template file.')
     print(f'Using "{args.to.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt))}" as recipient mail address.')
     print(f'Using "{args.url.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt))}" as URL template.')
+    print(f'Using "{args.createurl.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt))}" as URL template.')
     print(f'Using "{args.webserver}" as web server.')
     print(f'Using "{args.infix}" as infix.')
     print(f'Using "{args.smtpserver}" as SMTP server.')
@@ -158,44 +168,65 @@ def toHex(serial):
 def createMail(data):
     output = args.output.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash'], URL=data['URL']))
     directory = os.path.dirname(output)
-    print(f'Writing mail to {output}')
-    if not os.path.exists(directory):
-        os.makedirs(directory)
-    with open(output, 'wb') as mailfile:
-        template.stream(data).dump(mailfile, encoding='utf-8')
+
+    # Generate email body and attachments
     message = EmailMessage()
-    with open(output, 'r') as mailfile:
-        message.set_content(mailfile.read())
+    message.set_content(template.render(data))
+    for attach in args.attach:
+        with open(attach, 'rb') as attachment:
+            message.add_attachment(attachment.read(), *magic.from_file(attach, mime=True).split('/'))
+
     message['Date'] = formatdate(localtime=True)
     message['Subject'] = args.subject.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash'], URL=data['URL']))
     message['From'] = args.sender.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash'], URL=data['URL']))
-    message['Reply-To'] = args.replyto.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash'], URL=data['URL']))
+    if args.replyto:
+        message['Reply-To'] = args.replyto.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash'], URL=data['URL']))
     message['To'] = args.to.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash'], URL=data['URL']))
-    message['Bcc'] = ', '.join(args.bcc).format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash'], URL=data['URL']))
+    if args.cc:
+        message['Cc'] = ', '.join(args.cc).format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash'], URL=data['URL']))
+    if args.bcc:
+        message['Bcc'] = ', '.join(args.bcc).format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash'], URL=data['URL']))
+
+    # Record mail
+    print(f'Writing mail to {output}')
+    if not os.path.exists(directory):
+        os.makedirs(directory)
+    with open(output, 'w') as mailfile:
+        Generator(mailfile).flatten(message)
+
     if not args.dryrun:
+        # Actually send the mail
+        print(f'Sending mail to {message["To"]}')
+        if message['Cc']:
+            print(f'... and cc to {message["Cc"]}')
         if message['Bcc']:
-            print(f'Sending mail to {message["To"]} (and {message["Bcc"]})')
-        else:
-            print(f'Sending mail to {message["To"]}')
-        if args.starttls:
-            with smtplib.SMTP(args.smtpserver, args.smtpport) as smtp:
-                smtp.starttls()
-                smtp.login(args.smtpuser, args.smtppass)
-                smtp.send_message(message)
-        elif args.smtpuser or args.smtppass:
-            with smtplib.SMTP_SSL(args.smtpserver, args.smtpport) as smtp:
-                smtp.login(args.smtpuser, args.smtppass)
-                smtp.send_message(message)
-        else:
-            with smtplib.SMTP(args.smtpserver, args.smtpport) as smtp:
-                smtp.send_message(message)
-        print(f'Creating target at {data["URL"]}/create')
-        requests.get(f'{data["URL"]}/create')
+            print(f'... and bcc to {message["Bcc"]}')
+        try:
+            if args.starttls:
+                with smtplib.SMTP(args.smtpserver, args.smtpport) as smtp:
+                    smtp.starttls()
+                    smtp.login(args.smtpuser, args.smtppass)
+                    smtp.send_message(message)
+            elif args.smtpuser or args.smtppass:
+                with smtplib.SMTP_SSL(args.smtpserver, args.smtpport) as smtp:
+                    smtp.login(args.smtpuser, args.smtppass)
+                    smtp.send_message(message)
+            else:
+                with smtplib.SMTP(args.smtpserver, args.smtpport) as smtp:
+                    smtp.send_message(message)
+        except:
+            print(f'ERROR sending the mail!')
+
+        # Call the webhook if webserver is specified
+        if args.webserver:
+            print(f'Creating target at {data["CreateURL"]}')
+            requests.get(f'{data["CreateURL"]}')
 
 
 def generateHashAndURL(data):
     data['hash'] = hashlib.sha256(args.hashstring.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'])).encode('utf-8')).hexdigest()
     data['URL'] = args.url.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash']))
+    data['CreateURL'] = args.createurl.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt, site=data['site'], firstname=data['firstname'], lastname=data['lastname'], email=data['email'], hash=data['hash']))
 
 
 with open(args.input.format_map(SafeDict(basedir=args.basedir, campaign=args.campaign, infix=args.infix, webserver=args.webserver, salt=args.salt)), 'r') as inputfile: