diff --git a/docker/utils/Dockerfile b/docker/utils/Dockerfile index b3387ad1..43c38ab8 100644 --- a/docker/utils/Dockerfile +++ b/docker/utils/Dockerfile @@ -1,63 +1,73 @@ FROM apheleia/almalinux9 ENV HOME=/opt/app-root/src # Install troubleshooting utilities RUN dnf -y install \ --setopt 'tsflags=nodocs' \ bind-utils \ bzip2 \ cmake \ chromium \ chromedriver \ cyrus-imapd \ cyrus-sasl-plain \ expat-devel \ gcc \ git \ libjpeg-turbo-devel \ lsof \ net-tools \ nmap-ncat \ openldap-clients \ openssh-server \ php \ psmisc \ iputils \ mariadb \ procps-ng \ python3 \ python3-devel \ python3-pip \ python3-pytz \ strace \ telnet \ traceroute \ vim-enhanced \ wget \ + jq \ + ruby \ zlib-devel && \ dnf clean all RUN git clone https://github.com/libwbxml/libwbxml.git && \ cd libwbxml && \ mkdir build && \ cd build && \ cmake -D CMAKE_INSTALL_PREFIX=/usr .. && \ make && \ make install && \ + mv /usr/lib/libwbxml2.so* /usr/lib64/ && \ cd .. && \ rm -rf libwbxml RUN pip3 install distro xmltodict dnspython && \ pip3 install \ --global-option=build_ext \ --global-option="-I/usr/include/libwbxml-1.0/wbxml/" \ git+https://github.com/kanarip/python-wbxml#egg=wbxml +# imapcli deps +RUN gem install thor + RUN id default || (groupadd -g 1001 default && useradd -d /opt/app-root/ -u 1001 -g 1001 default) +RUN chgrp -R 0 /opt/app-root/src && \ + chmod -R g=u /opt/app-root/src && \ + chown -R 1001:0 /opt/app-root/src + USER 1001 WORKDIR ${HOME} COPY /rootfs / diff --git a/docker/utils/rootfs/opt/app-root/src/imapcli.rb b/docker/utils/rootfs/opt/app-root/src/imapcli.rb new file mode 100755 index 00000000..2cecabb8 --- /dev/null +++ b/docker/utils/rootfs/opt/app-root/src/imapcli.rb @@ -0,0 +1,157 @@ +#!/usr/bin/env ruby + +require "net/imap" +require 'thor' + +class IMAP < Net::IMAP + + def send_command2(cmd, *args, &block) + synchronize do + tag = generate_tag + put_string(tag + " " + cmd + CRLF) + + begin + return get_tagged_response(tag, cmd) + ensure + if block + remove_response_handler(block) + end + end + end + end + + def getmetadata(mailbox, *entries) + synchronize do + data = '(' + entries.join(' ') + ')' + send_command("GETMETADATA" + " (DEPTH infinity)", mailbox, RawData.new(data)) + + result = @responses.delete("METADATA") + if result and result.length() > 0 + return result[-1] + end + return "" + end + end + + def setmetadata(mailbox, entry, value) + data = '(' + entry + ' ' + '"' + IMAP.encode_utf7(value) + '"' + ')' + send_command("SETMETADATA", mailbox, RawData.new(data)) + end +end + + +class ImapCli < Thor + class_option :host + class_option :port + class_option :username + class_option :password + class_option :ssl, :type => :boolean + class_option :debug, :type => :boolean + + no_commands { + def imap() + if !@imap + if options[:ssl] + @imap = IMAP.new(options[:host], :port => options[:port], :ssl => { + :verify_mode => OpenSSL::SSL::VERIFY_NONE + }) + else + @imap = IMAP.new(options[:host], :port => options[:port], :ssl => false) + end + if options[:debug] + IMAP.debug = true + end + @imap.login(options[:username], options[:password]) + end + @imap + end + } + + desc "login", "Login." + def login() + imap() + end + + desc "list", "List." + def list(folder = "**") + p imap.list("", folder) + end + + desc "lsub", "List subscriptions." + def lsub(folder = "**") + p imap.lsub("", folder) + end + + desc "namespace", "Namespace." + def namespace() + p imap.namespace() + end + + desc "capability", "Capability." + def capability() + p imap.capability() + end + + desc "select", "Select." + def select(folder) + p imap.select(folder) + end + + desc "create", "Create." + def create(folder) + p imap.create(folder) + end + + desc "delete", "Delete." + def delete(folder) + p imap.delete(folder) + end + + desc "subscribe", "Subscribe." + def subscribe(folder) + p imap.subscribe(folder) + end + + desc "getmetadata", "Getmetadata." + def getmetadata(folder, *entries) + # p imap.select(folder) + p imap.getmetadata(folder, entries) + end + + desc "idle", "IDLE." + def idle(folder, *entries) + p imap.select(folder) + imap.send_command2("IDLE") + end + + desc "getacl", "getacl." + def getacl(folder) + p imap.getacl(folder) + end + + desc "setmetadata", "Setmetadata." + def setmetadata(folder, entry, value) + p imap.setmetadata(folder, entry, value) + end + + desc "download", "Download." + def download(folder, destination) + imap.select(folder) + #Dir.mkdir destination unless File.exists? destination + Dir.mkdir destination + imap.uid_fetch(1..-1, "RFC822").each do |mail| + uid = mail.attr["UID"] + p uid + File.write("#{destination}#{uid}.", mail.attr["RFC822"]) + end + end + +end + +begin + ImapCli.start(ARGV) +rescue => e + puts e.message + puts e.backtrace.inspect + raise e +end diff --git a/docker/utils/rootfs/opt/app-root/src/mailtransporttest.py b/docker/utils/rootfs/opt/app-root/src/mailtransporttest.py index 75168fc6..29b92136 100755 --- a/docker/utils/rootfs/opt/app-root/src/mailtransporttest.py +++ b/docker/utils/rootfs/opt/app-root/src/mailtransporttest.py @@ -1,142 +1,142 @@ #!/bin/env python3 """ Send an email via SMTP and then look for it via IMAP. ./mailtransporttest.py --sender-username test1@kolab.org --sender-password foobar --sender-host smtp.kolabnow.com --recipient-username test2@kolab.org --recipient-password foobar --recipient-host imap.kolabnow.com """ from datetime import datetime import argparse import sys import imaplib import smtplib import uuid import time mailtemplate = ''' MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=_291b8e96564265636432c6d494e02322" Date: {date} From: {sender} To: {to} Subject: {subject} Message-ID: {messageid} --=_291b8e96564265636432c6d494e02322 Content-Type: multipart/alternative; boundary="=_ceff0fd19756f45ed1295ee2069ff8e0" --=_ceff0fd19756f45ed1295ee2069ff8e0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset=US-ASCII sdlkjsdjf --=_ceff0fd19756f45ed1295ee2069ff8e0 Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=UTF-8

sdlkjsdjf

--=_ceff0fd19756f45ed1295ee2069ff8e0-- --=_291b8e96564265636432c6d494e02322 Content-Transfer-Encoding: base64 Content-Type: text/plain; name=xorg.conf Content-Disposition: attachment; filename=xorg.conf; size=211 U2VjdGlvbiAiRGV2aWNlIgogICAgSWRlbnRpZmllciAgICAgIkRldmljZTAiCiAgICBEcml2ZXIg {attachment}ICAgIEJvYXJkTmFtZSAgICAgICJOVlMgNDIwME0iCiAgICBPcHRpb24gIk5vTG9nbyIgInRydWUi CiAgICBPcHRpb24gIlVzZUVESUQiICJ0cnVlIgpFbmRTZWN0aW9uCg== --=_291b8e96564265636432c6d494e02322-- '''.strip() class SendTest: def __init__(self, options): self.recipient_host = options.recipient_host self.recipient_username = options.recipient_username self.recipient_password = options.recipient_password self.sender_host = options.sender_host self.sender_username = options.sender_username self.sender_password = options.sender_password self.uuid = str(uuid.uuid4()) self.subject = f"Delivery Check {self.uuid}" def check_for_mail(self): print(f"Checking for uuid {self.uuid}") imap = imaplib.IMAP4_SSL(host=self.recipient_host, port=993) imap.login(self.recipient_username, self.recipient_password) imap.select("INBOX") typ, data = imap.search(None, 'SUBJECT', '"' + self.subject + '"') for num in data[0].split(): print(f"Found the mail with uid {num}") imap.store(num, '+FLAGS', '\\Deleted') imap.expunge() return True return False def send_mail(self, starttls): dtstamp = datetime.utcnow() msg = mailtemplate.format( - messageid="<{}@deliverycheck.org>".format(uuid), + messageid="<{}@deliverycheck.org>".format(self.uuid), subject=self.subject, sender=self.sender_username, to=self.recipient_username, date=dtstamp.strftime("%a, %d %b %Y %H:%M:%S %z"), attachment='ICAgIEJvYXJkTmFtZSAgICAgICJOVlMgNDIwME0iCiAgICBPcHRpb24gIk5vTG9nbyIgInRydWUi\n' ) if starttls: with smtplib.SMTP(host=self.sender_host, port=587) as smtp: smtp.starttls() smtp.ehlo() smtp.login(self.sender_username, self.sender_password) smtp.noop() smtp.sendmail(self.sender_username, self.recipient_username, msg) print(f"Email with uuid {self.uuid} sent") else: with smtplib.SMTP_SSL(host=self.sender_host, port=465) as smtp: smtp.login(self.sender_username, self.sender_password) smtp.noop() smtp.sendmail(self.sender_username, self.recipient_username, msg) print(f"Email with uuid {self.uuid} sent") parser = argparse.ArgumentParser(description='Mail transport tests.') parser.add_argument('--sender-username', help='The SMTP sender username') parser.add_argument('--sender-password', help='The SMTP sender password') parser.add_argument('--sender-host', help='The SMTP sender host') parser.add_argument('--recipient-username', help='The IMAP recipient username') parser.add_argument('--recipient-password', help='The IMAP recipient password') parser.add_argument('--recipient-host', help='The IMAP recipient host') parser.add_argument('--timeout', help='Timeout in minutes', type=int, default=10) parser.add_argument("--starttls", action='store_true', help="Use starttls over 587") args = parser.parse_args() obj = SendTest(args) obj.send_mail(args.starttls) timeout = 10 for i in range(1, round(args.timeout * 60 / timeout) + 1): if obj.check_for_mail(): print("Success!") sys.exit(0) print(f"waiting for {timeout}") time.sleep(timeout) print("Failed to find the mail") sys.exit(1)