Mailabruf von externen Servern

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
sharbich
User
Beiträge: 9
Registriert: Freitag 15. März 2019, 15:38

Hallo Ihr Lieben,
ich versuche ein Programm zu starten und bekomme die Meldung:

Code: Alles auswählen

secmail@dsme01:/tmp/test$ ./getmail-ldap-new.py 
Cannot find all required libraries please install them and try again
Mit folgenden Befehl die benötigten Libraries ermitteln:

Code: Alles auswählen

secmail@dsme01:/tmp/test$ pip freeze > requirements.txt
das sind die folgenden:

Code: Alles auswählen

secmail@dsme01:/tmp/test$ cat requirements.txt 
certifi==2022.12.7
charset-normalizer==2.1.1
docopt==0.6.2
idna==3.4
pipreqs==0.4.11
requests==2.28.1
urllib3==1.26.13
yarg==0.1.9
Mit folgenden Befehl installiere ich Sie:

Code: Alles auswählen

secmail@dsme01:/tmp/test$ pip install -r requirements.txt
Defaulting to user installation because normal site-packages is not writeable
Requirement already satisfied: certifi==2022.12.7 in /usr/local/lib/python3.10/site-packages (from -r requirements.txt (line 1)) (2022.12.7)
Requirement already satisfied: charset-normalizer==2.1.1 in /usr/local/lib/python3.10/site-packages (from -r requirements.txt (line 2)) (2.1.1)
Requirement already satisfied: docopt==0.6.2 in /usr/local/lib/python3.10/site-packages (from -r requirements.txt (line 3)) (0.6.2)
Requirement already satisfied: idna==3.4 in /usr/local/lib/python3.10/site-packages (from -r requirements.txt (line 4)) (3.4)
Requirement already satisfied: pipreqs==0.4.11 in /usr/local/lib/python3.10/site-packages (from -r requirements.txt (line 5)) (0.4.11)
Requirement already satisfied: requests==2.28.1 in /usr/local/lib/python3.10/site-packages (from -r requirements.txt (line 6)) (2.28.1)
Requirement already satisfied: urllib3==1.26.13 in /usr/local/lib/python3.10/site-packages (from -r requirements.txt (line 7)) (1.26.13)
Requirement already satisfied: yarg==0.1.9 in /usr/local/lib/python3.10/site-packages (from -r requirements.txt (line 8)) (0.1.9)
Führe ich den obigen Befehl dann wieder aus kommt die gleiche Fehlermeldung:

Code: Alles auswählen

secmail@dsme01:/tmp/test$ ./getmail-ldap-new.py 
Cannot find all required libraries please install them and try again
Was mache ich falsch?
Hier das Skript:

Code: Alles auswählen

secmail@dsme01:/tmp/test$ cat getmail-ldap-new.py 
#!/usr/bin/python
# File: getmail-ldap.py
try:
    import errno
    import string
    import logging
    import logging.handlers
    import ldap
    import Configparser
    import ldif
    import threading
    from StringIO import StringIO
    from to import StringIO
    from os.path import os
    from subprocess import Popen,PIPE
except ImportError:
    print("""Cannot find all required libraries please install them and try again""")
    raise SystemExit

config_file_location = '/home/secmail/getmail-ldap.cfg'

def pid_exists(pid):
    """Is there a process with PID pid?"""
    if pid < 0:
        return False

    exist = False
    try:
        os.kill(pid, 0)
        exist = 1
    except OSError as x:
        if x.errno != errno.ESRCH:
            raise

    return exist

def get_search_results(results):
    """Given a set of results, return a list of LDAPSearchResult
    objects.
    """
    res = []

    if type(results) == tuple and len(results) == 2 :
        (code, arr) = results
    elif type(results) == list:
        arr = results

    if len(results) == 0:
        return res

    for item in arr:
        res.append( LDAPSearchResult(item) )

    return res

class LDAPSearchResult:
    """A class to model LDAP results.
    """

    dn = ''

    def __init__(self, entry_tuple):
        """Create a new LDAPSearchResult object."""
        (dn, attrs) = entry_tuple
        if dn:
            self.dn = dn
        else:
            return

        self.attrs = cidict(attrs)

    def get_attributes(self):
        """Get a dictionary of all attributes.
        get_attributes()->{'name1':['value1','value2',...],
				'name2: [value1...]}
        """
        return self.attrs

    def set_attributes(self, attr_dict):
        """Set the list of attributes for this record.

        The format of the dictionary should be string key, list of
        string alues. e.g. {'cn': ['M Butcher','Matt Butcher']}

        set_attributes(attr_dictionary)
        """

        self.attrs = cidict(attr_dict)

    def has_attribute(self, attr_name):
        """Returns true if there is an attribute by this name in the
        record.

        has_attribute(string attr_name)->boolean
        """
        return attr_name in self.attrs

    def get_attr_values(self, key):
        """Get a list of attribute values.
        get_attr_values(string key)->['value1','value2']
        """
        return self.attrs[key]

    def get_attr_names(self):
        """Get a list of attribute names.
        get_attr_names()->['name1','name2',...]
        """
        return list(self.attrs.keys())

    def get_dn(self):
        """Get the DN string for the record.
        get_dn()->string dn
        """
        return self.dn

    def pretty_print(self):
        """Create a nice string representation of this object.

        pretty_print()->string
        """
        str = "DN: " + self.dn + "\n"
        for a, v_list in self.attrs.items():
            str = str + "Name: " + a + "\n"
            for v in v_list:
                str = str + "  Value: " + v + "\n"
        str = str + "========"
        return str

    def to_ldif(self):
        """Get an LDIF representation of this record.

        to_ldif()->string
        """
        out = StringIO()
        ldif_out = ldif.LDIFWriter(out)
        ldif_out.unparse(self.dn, self.attrs)
        return out.getvalue()

class RetrieveMails(threading.Thread):
    def __init__(self, getmail_binary, config_filename, config_data_dir):
        threading.Thread.__init__(self)
        self.getmail_binary, self.config_filename, self.config_data_dir = \
            getmail_binary, config_filename, config_data_dir
    def run(self):
        try:
            command = [self.getmail_binary, \
                #'--quiet', \
                '--rcfile=' + self.config_filename, \
                '--getmaildir=' + self.config_data_dir]
            self.pid_filename = self.config_filename + '.pid'
            # Check for a pidfile to see if the daemon already runs
            try:
                pid_file = file(self.pid_filename,'r')
                pid_number = pid = int(pid_file.read().strip())
                pid_file.close()
            except IOError:
                pid = None
            # Check whether process is really running
            if pid:
                pid = pid_exists(pid)
            if not pid:
                getmail_process = Popen(command, shell=False,stdout=PIPE,stderr=PIPE)
                try:
                    file(self.pid_filename,'w+').write("%s\n" % getmail_process.pid)
                    getmail_process.wait()
                finally:
                    os.remove(self.pid_filename)
                    # Zur Sicherheit die erstellte Konfigurationsdatei loeschen (Login-Daten!)
                    os.remove(self.config_filename)
                stderr_output=string.join(getmail_process.stderr.readlines())
                if getmail_process.returncode != 0 or len(stderr_output.strip())>0 :
                    raise Exception("Getmail command failed for " + " ".join(command) \

                        +"\nStdErr: \n" + string.join(stderr_output.strip()) \
                        +"\nStdOut: \n" + string.join(getmail_process.stdout.readlines()))
            else:
                log_object.info("Command " + " ".join(command) +\
                    " not executed, existing pid " + str(pid_number) + " found")
        except:
            log_object.exception("An error occured!")

class RetrieveAccount:
    account_name = None
    account_type = None
    login = None
    password = None
    server = None
    def __init__(self, account_name=None, account_type=None, server=None, login=None, password=None):
        self.account_name, self.account_type, self.login, self.password, self.server = \
            account_name, account_type, login, password, server

class GetmailConfigFile(Configparser.SafeConfigParser):
    output_filename = None
    def __init__(self, defaults, default_config_filename=None, output_filename=None):
        configParser.SafeConfigParser.__init__(self, defaults)
        if default_config_filename is not None:
            self.read(default_config_filename)
        self.output_filename = output_filename
    def set_pop3_account(self, newRetrieveAccount):
        self.set('retriever','server',newRetrieveAccount.server)
        self.set('retriever','type',newRetrieveAccount.account_type)
        self.set('retriever','username',newRetrieveAccount.login)
        self.set('retriever','password',newRetrieveAccount.password)
        self.set('destination','arguments','("-i", "stefan.harbich@harnet.de",)')
    def write(self):
        if self.output_filename is not None:
            """try:
                output_file = open(self.output_filename, 'wb')
            except:
                raise Exception, "Unable to open " + \
                    self.output_filename + "for writing"
            finally:
                output_file.close()
            """
            os.umask(0o077)
            output_file = open(self.output_filename, 'wb')
            configParser.SafeConfigParser.write(self, output_file)
        else:
            raise Exception("No output file for configuration defined")

# Konfigurationsdatei lesen
config_object = Configparser.SafeConfigParser()
config_object.read(config_file_location)

# Set-up Logging
log_object = logging.getLogger("getmail-ldap")
## log_object.setLevel(logging.DEBUG)
log_object.setLevel(logging.ERROR)

# This handler writes everything to a log file.
log_file_handler = logging.FileHandler(config_object.get('Logging','LogFile'))
log_file_formatter = logging.Formatter("%(levelname)s %(asctime)s %(funcName)s %(lineno)d %(message)s")
log_file_handler.setFormatter(log_file_formatter)
## log_file_handler.setLevel(logging.DEBUG)
log_file_handler.setLevel(logging.ERROR)
log_object.addHandler(log_file_handler)

# This handler emails anything that is an error or worse.
log_smtp_handler = logging.handlers.SMTPHandler(\
    config_object.get('Logging','MailServer'),\
    config_object.get('Logging','MailFrom'),\
    config_object.get('Logging','MailTo').split(','),\
    config_object.get('Logging','MailSubject'))
log_smtp_handler.setLevel(logging.ERROR)
log_smtp_handler.setFormatter(log_file_formatter)
log_object.addHandler(log_smtp_handler)

def main_call():

    ## first you must open a connection to the LDAP server
    ldap_object = ldap.initialize(config_object.get('LDAP','LDAPServer'))
    ldap_object.simple_bind_s(\
        config_object.get('LDAP','BindDN'),\
        config_object.get('LDAP','BindPassword'))
    # searching doesn't require a bind in LDAP V3.
    # If you're using LDAP v2, set the next line appropriately
    # and do a bind as shown in the above example.
    # you can also set this to ldap.VERSION2 if you're using a v2 directory
    # you should  set the next option to ldap.VERSION2 if you're using a v2 directory
    ldap_object.protocol_version = ldap.VERSION3

    ## The next lines will also need to be changed to support your search requirements and directory
    ## retrieve all attributes - again adjust to your needs - see documentation for more options

    if config_object.get('LDAP','SearchScope').upper() == "SUB":
        search_scope = ldap.SCOPE_SUBTREE
    elif config_object.get('LDAP','SearchScope').upper() == "ONE":
        search_scope = ldap.SCOPE_ONELEVEL
    else:
        search_scope = ldap.SCOPE_BASE

    ldap_result_id = ldap_object.search( \
        config_object.get('LDAP','SearchDN'), \
        search_scope,
        config_object.get('LDAP','SearchFilter'), \
        None)

    ldap_results = []

    while 1:
        result_type, result_data = ldap_object.result(ldap_result_id, 0)
        if (result_data == []):
            break
        else:
            ## here you don't have to append to a list
            ## you could do whatever you want with the individual entry
            ## The appending to list is just for illustration.
            if result_type == ldap.RES_SEARCH_ENTRY:
                ldap_results += get_search_results(result_data)
    for ldap_result in ldap_results:
        account = RetrieveAccount( \
            # Account Name \
            ldap_result.get_attr_values(\
                config_object.get('LDAP','RelevantAttributes').split(',')[0])[0] ,\
            # Account Type \
            ldap_result.get_attr_values(\
                config_object.get('LDAP','RelevantAttributes').split(',')[1])[0],\
            # Server \
            ldap_result.get_attr_values(\
                config_object.get('LDAP','RelevantAttributes').split(',')[2])[0],\
            # Login \
            ldap_result.get_attr_values(\
                config_object.get('LDAP','RelevantAttributes').split(',')[3])[0],\
            # Password \
            ldap_result.get_attr_values(\
                config_object.get('LDAP','RelevantAttributes').split(',')[4])[0]\
        )
        config_output_filename = os.path.join(\
            config_object.get('Main','ConfigFileOutputDir'), \
            "getmail_" + \
            account.account_name + \
            ".cfg")
        config_file = GetmailConfigFile(None, \
            config_object.get('Main','DefaultGetmailConfigFile'), config_output_filename)
        config_file.set_pop3_account(account)
        log_object.info("Writing Account Configuration for " + account.account_name + \
                " to file " + config_output_filename)
        config_file.write()
        RetrieveMails(\
            config_object.get('Main','GetmailBinary'), \
            config_output_filename, \
            config_object.get('Main','GetmailDir')\
        ).start()
        #print config_output_filename
        #print "Name " + account.account_name
        #print "Type " + account.account_type
        #print "Server " + account.server
        #print "Login " + account.login
        #print "Password " + account.password
        #print "-----------------"
        #print ldap_result.pretty_print()

if __name__ == "__main__":
    try:
        main_call();
    except:
        log_object.exception("An error occured!")
Benutzeravatar
sparrow
User
Beiträge: 4535
Registriert: Freitag 17. April 2009, 10:28

Du entfernst den try-except-Bock um die Importe und lässt den dauerhaft weg. Denn der verschluckt die Fehlermeldung, die du eigentlich sehen willst.
Es macht keinen Sinn eine Ausnahme zu fangen, nicht abzuarbeiten und durch eine nichtssagende Ausgabe zu ersetzen.

Was soll denn das Klassenattribut "dn" in LDAPSearchResult? Warum ist das ein Klassenattribut und nicht an die Instanz gebunden?

Keine! nackten! Excepts!
Der try-Except-Block im if __name__ == .... -Block muss weg. Auch der fängt Fehlermeldungen und ersetzt sie durch eine nichtssagende Ausgabe.

Dateien öffnet man mit dem with-statement. Das hat den Vorteil, dass man sich nicht um das Schließen kümmern muss. Was du hier auch nicht tust.
Benutzeravatar
__blackjack__
User
Beiträge: 14019
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Der Fehler ist übrigens ein falsch getippter Buchstabe. :-)
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
sharbich
User
Beiträge: 9
Registriert: Freitag 15. März 2019, 15:38

__blackjack__ hat geschrieben: Donnerstag 29. Dezember 2022, 22:52 Der Fehler ist übrigens ein falsch getippter Buchstabe. :-)
Bist Du so lieb und hilfst mir weiter? Dankeschön.
Benutzeravatar
grubenfox
User
Beiträge: 606
Registriert: Freitag 2. Dezember 2022, 15:49

Vermutlich steht dazu hilfreiches in der/den Fehlermeldung/en, die man nach
sparrow hat geschrieben: Donnerstag 29. Dezember 2022, 20:46 Du entfernst den try-except-Bock um die Importe und lässt den dauerhaft weg. Denn der verschluckt die Fehlermeldung, die du eigentlich sehen willst.
Es macht keinen Sinn eine Ausnahme zu fangen, nicht abzuarbeiten und durch eine nichtssagende Ausgabe zu ersetzen.
und
sparrow hat geschrieben: Donnerstag 29. Dezember 2022, 20:46 Keine! nackten! Excepts!
Der try-Except-Block im if __name__ == .... -Block muss weg. Auch der fängt Fehlermeldungen und ersetzt sie durch eine nichtssagende Ausgabe.
zu sehen bekommt.
list_comprehension
User
Beiträge: 3
Registriert: Dienstag 27. Dezember 2022, 10:36

sharbich hat geschrieben: Samstag 31. Dezember 2022, 00:35
__blackjack__ hat geschrieben: Donnerstag 29. Dezember 2022, 22:52 Der Fehler ist übrigens ein falsch getippter Buchstabe. :-)
Bist Du so lieb und hilfst mir weiter? Dankeschön.
Wenn du den try/except Block (warum auch immer) nicht (temporär) entfernen willst, gäbe es ja noch die Möglichkeit sich die Error Message ausgeben zu lassen. Wenn du eine selbst definierte Message ausgibst, sollte daraus auch hervorgehen wo das Problem liegt.

Falls du Langeweile hast, gäbe es auch noch die Möglichkeit eines separaten try/except Blocks für jeden import.
Benutzeravatar
snafu
User
Beiträge: 6854
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

__blackjack__ hat geschrieben: Donnerstag 29. Dezember 2022, 22:52 Der Fehler ist übrigens ein falsch getippter Buchstabe. :-)
Und woanders ein großer, der klein sein müsste (außer es wurde ein eigenes Modul tatsächlich so genannt).
Sirius3
User
Beiträge: 18260
Registriert: Sonntag 21. Oktober 2012, 17:20

Nein, ein kleiner der groß sein müßte. Es handelt sich ja um ein Python2-Programm.
sharbich
User
Beiträge: 9
Registriert: Freitag 15. März 2019, 15:38

Hallo Ihr Lieben,
ich Denke Ihr habt meinen Äußerungen schon entnommen das ich kein "Developer" bin der die Python Sprache beherrscht.
Ich ahne das mir deshalb auch nicht weitergeholfen wird? Falls das so ist dann kann ich das Thema hier beenden. Leider habe ich nur wenig Halbwissen, wäre aber daran interessiert weiter dieses aus zu bauen. Tja da ändert sich von Python 2 zu 3 beim print Befehl etwas mit " und (), es ist darauf zu achten die Variablen richtig einzurücken, etc. eben nur Halbwissen.
Wäre ein Mentor bereit mir weiter zu leiten? Um langfristig mein Problem zu lösen?
Gruß von Stefan
sharbich
User
Beiträge: 9
Registriert: Freitag 15. März 2019, 15:38

Hallo,
kann ich überhaupt das Python 2 Skript mit der Python Version 3.10 starten? Wenn ich das mache bekomme ich folgende Fehlermeldungen:

Code: Alles auswählen

secmail@dsme01:~$ python3.10 getmail-ldap.py
/home/secmail/getmail-ldap.py:219: DeprecationWarning: The SafeConfigParser class has been renamed to ConfigParser in Python 3.2. This alias will be removed in future versions. Use ConfigParser directly instead.
  config_object = configparser.SafeConfigParser()
--- Logging error ---
Traceback (most recent call last):
  File "/home/secmail/getmail-ldap.py", line 332, in <module>
    main_call();
  File "/home/secmail/getmail-ldap.py", line 286, in main_call
    ldap_results += get_search_results(result_data)
  File "/home/secmail/getmail-ldap.py", line 49, in get_search_results
    res.append( LDAPSearchResult(item) )
  File "/home/secmail/getmail-ldap.py", line 67, in __init__
    self.attrs = cidict(attrs)
NameError: name 'cidict' is not defined

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.10/logging/handlers.py", line 1046, in emit
    smtp.send_message(msg)
  File "/usr/local/lib/python3.10/smtplib.py", line 986, in send_message
    return self.sendmail(from_addr, to_addrs, flatmsg, mail_options,
  File "/usr/local/lib/python3.10/smtplib.py", line 887, in sendmail
    raise SMTPSenderRefused(code, resp, from_addr)
smtplib.SMTPSenderRefused: (530, b'5.7.0 Must issue a STARTTLS command first', 'secmail@harnet.de')
Call stack:
  File "/home/secmail/getmail-ldap.py", line 334, in <module>
    log_object.exception("An error occured!")
Message: 'An error occured!'
Arguments: ()
Sirius3
User
Beiträge: 18260
Registriert: Sonntag 21. Oktober 2012, 17:20

Dir wird geholfen, wie Du Dir selbst helfen kannst. Wenn Du etwas an den Antworten nicht verstehst, mußt Du nur nochmal konkret nachfragen.
Insgesamt ist das Programm, dass Du Dir da gesucht hast, eher schlecht programmiert, es ist ein Python2-Programm dass nur mit großem Aufwand nach Python3 migriert werden kann und egal, ob das nun Python2 oder 3 ist, cidict ist wirklich nicht definiert, das Programm ist also zumindest unvollständig.

Daher nochmal ein paar Schritte zurück: was ist Dein eigentliches Problem? Wie hast Du das Programm gefunden, und warum denkst Du, dass es Dein Problem löst?
Benutzeravatar
__blackjack__
User
Beiträge: 14019
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@sharbich: Dir wird schon weitergeholfen, nur kannst Du mit der Hilfe offenbar (noch) nichts anfangen. Wobei ich da jetzt aus Deinen Aussagen noch nicht so ganz schlau werde ob Du Python nun lernen willst oder nicht.

Die Hilfe ist nicht in Form einer schlüsselfertigen Lösung, sondern als Ratschlag, dass man keine Ausnahmebehandlung macht die Fehler einfach ”verschluckt” oder durch Ausgaben ersetzt die einfach nur sagen *dass* etwas falsch gelaufen ist, aber nicht *was* da falsch gelaufen ist. Das heisst wenn Du diese ”Fehlerbehandlung(en)” aus dem Quelltext raus nimmst, sind wir (ich denke ich kann da auch für die anderen sprechen) davon ausgegangen, dass Du die von uns entdeckten Tippfehler leicht selbst findest, weil die ungefilterten Ausnahmen da sehr deutlich sind und der Fehler dann trivial zu beheben ist, auch von Programmieranfängern.

Beim aktuellen Fehler hast Du Glück, das in Python 3 Ausnahmen verkettet werden und nicht nur die letzte Ausnahme im Traceback steht, sondern auch Ausnahmen die während einer Ausnahmebehandlung auftraten. Denn sonst hätten wir da genau das gleiche Problem, dass einfach nur „Message: 'An error occured!'“ ausgegeben würde, was nicht wirklich hilfreich ist. Das wurde aber auch schon in der ersten Antwort gesagt, dass diese Ausnahmebehandlung in dem ``if __name__ …``-Zweig weg muss, wenn man sinnvoll auf Fehlersuche gehen will.

Die Ausnahme die das auslöst, entsteht durch ein verwendetes, aber nirgends definiertes `cidict()`. Da muss man jetzt raten was der Autor damit gemeint haben könnte und wo das herkommen soll. Ich vermute mal das soll ein Wörterbuch sein, dass „case insensitive“ ist, also wo die Schlüssel Zeichenketten sind, es aber egal ist wie da die Gross-/Kleinschreibung ist.

Jetzt mal abgesehen von der Portierung von Python 2 nach Python 3 ist da so einiges komisch bis kaputt in dem Quelltext. Das sieht nach einem Anfänger aus, mindestens in Python. Das ist deutlich überarbeitungswürdig.

Und dann kommt noch die Portierung von Python 2 nach Python 3 dazu. Dazu muss man programmieren können, und nicht nur Python 3, sondern genug von Python 2 verstehen, damit man weiss was man da ändern muss und warum. Das ist machbar, aber eben nicht sofort. Es wäre jedenfalls nicht das was ich einem Anfänger als erstes Projekt empfehlen würde.

Und ich denke da wird auch niemand so einfach alle Fehler finden und beheben und das portieren können, weil letztlich Testläufe gemacht werden müssen, und das Programm nicht so ohne weiteres ohne eine entsprechende Testumgebung mit LDAP und E-Mail-Servern läuft. Das ist also keine Frage ob/dass Dir niemand helfen *will*, sondern eher dass das kein kleines/sehr einfaches Problem ist, und das man das nicht losgelöst von einer bestimmten Umgebung in der das laufen muss, testen kann.
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
sharbich
User
Beiträge: 9
Registriert: Freitag 15. März 2019, 15:38

Hallo Ihr Lieben,

lieben Dank für Eure Hinweise. Ich habe angefangen mich mit Python zu beschäftigen. Hierzu schaue ich mir eine Video Serie mit dem Namen "Python Tutorial deutsch [1/24] - Dein erstes Programm" an. Sobald ich mich traue versuche ich mein gefundenes Projekt anzupassen.
Wie Ihr schon richtig vermutet habt betreibe ich in meiner privaten Cloud zu Hause mehrere Dienste und eine eigene Mail Infrastruktur. Ich möchte mit dem Skript den neuen Mail Kunden die Möglichkeit geben Ihre vorhandenen Mail Account abzurufen um diese dem neuen Mailaccount hinzu zu fügen.
Unter Python 2.7 hat das Skript noch funktioniert.

Sobald ich mich traue das Projekt anzugehen melde ich mich hier wieder.

Gruß von Stefan
Antworten