Al-HUWAITI Shell
Al-huwaiti


Server : LiteSpeed
System : Linux in-mum-web1949.main-hosting.eu 5.14.0-503.40.1.el9_5.x86_64 #1 SMP PREEMPT_DYNAMIC Mon May 5 06:06:04 EDT 2025 x86_64
User : u595547767 ( 595547767)
PHP Version : 7.4.33
Disable Function : NONE
Directory :  /opt/alt/python27/lib/python2.7/site-packages/postomaat/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : //opt/alt/python27/lib/python2.7/site-packages/postomaat/service.py
# -*- coding: utf-8 -*-
#   Copyright 2012-2018 Oli Schacher
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

try:
    import ConfigParser
except ImportError:
    import configparser as ConfigParser
import hashlib
import logging
import logging.config
import multiprocessing
import optparse
import os
import signal
import sys

import postomaat.funkyconsole
from postomaat import __version__
from postomaat.addrcheck import Addrcheck
from postomaat.checkLogfileConfig import checkLogfileConfig
from postomaat.core import MainController
from postomaat.daemon import DaemonStuff

controller = None
theconfigfile = '/etc/postomaat/postomaat.conf'
dconfdir = '/etc/postomaat/conf.d'
theloggingfile = '/etc/postomaat/logging.conf'
thepidfile = '/var/run/postomaat.pid'


def reloadconfig():
    """reload configuration file"""

    logger = logging.getLogger('postomaat')
    if controller:
        if controller.threadpool is None and controller.procpool is None:
            logger.error("""No process/threadpool -> This is not the main process!
                          \nNo changes will be applied...!\nSend SIGHUP to the main process!
                          \nCurrent controller object : %s, %s""" % (id(controller),controller.__repr__()))
            return
    else:
        logger.error("No controller -> This is not the main process!\n"
                     "No changes will be applied...!\nSend SIGHUP to the main process!")
        return

    if controller.logProcessFacQueue is None:
        logger.error("No log process in controller -> This is not the main process!\n"
                     "No changes will be applied...!\nSend SIGHUP to the main process!")
        return

    assert controller.logConfigFileUpdates is not None
    assert controller.configFileUpdates is not None

    configFileUpdates    = getConfigFileUpdatesDict(theconfigfile,dconfdir)
    logConfigFileUpdates = getLogConfigFileUpdatesDict(theloggingfile)

    logger.info('Number of messages in logging queue: %u'%controller.logQueue.qsize())

    logger.info("Log config has changes: %s" % str(logConfigFileUpdates != controller.logConfigFileUpdates))
    logger.info("Main config has changes: %s" % str(configFileUpdates != controller.configFileUpdates))

    if logConfigFileUpdates != controller.logConfigFileUpdates:
        # save back log config file dict for later use
        controller.logConfigFileUpdates = logConfigFileUpdates

        logger.info("Create new log process with new configuration")
        logProcessFacQueue = controller.logProcessFacQueue
        newLogConfigure = postomaat.logtools.logConfig(logConfigFile=theloggingfile)
        logProcessFacQueue.put(newLogConfigure)


    if configFileUpdates != controller.configFileUpdates:
        logger.info('Reloading configuration')

        # save back config file dict for later use
        controller.configFileUpdates = configFileUpdates
        newconfig = ConfigParser.RawConfigParser()
        with open(theconfigfile) as fp:
            newconfig.readfp(fp)

        # Setup address compliance checker
        try:
            address_check = newconfig.get('main','address_compliance_checker')
        except Exception as e:
            # might happen for some tests which do not propagate defaults
            address_check = "Default"
        Addrcheck().set(address_check)

        identifier = "no identifier given"
        if newconfig.has_option('main', 'identifier'):
            identifier = newconfig.get('main', 'identifier')

        # load conf.d
        if os.path.isdir(dconfdir):
            filelist = os.listdir(dconfdir)
            configfiles = [
                dconfdir + '/' + c for c in filelist if c.endswith('.conf')]
            logger.debug('Conffiles in %s: %s' % (dconfdir, configfiles))
            readfiles = newconfig.read(configfiles)
            logger.debug('Read additional files: %s' % (readfiles))

        logger.info('Reload config complete. Current configuration:%s' %
                    identifier)
        controller.config = newconfig
        controller.propagate_core_defaults()

        logger.info('Reloading plugins...')
        ok = controller.load_plugins()
        if ok:
            logger.info('Plugin reload completed')
        else:
            logger.error('Plugin reload failed')

        controller.propagate_plugin_defaults()

        controller.reload()


def sighup(signum, frame):
    """handle sighup to reload config"""
    reloadconfig()

def getConfigFileUpdatesDict(configfilename,dconfigFileDir):
    configfiles = [configfilename]
    # load conf.d
    if dconfigFileDir and os.path.isdir(dconfigFileDir):
        filelist = os.listdir(dconfigFileDir)
        configfiles.extend([dconfigFileDir + '/' + c for c in filelist if c.endswith('.conf')])

    hashlist = createMD5(configfiles)
    configFileUpdates = dict(zip(configfiles, hashlist))
    return configFileUpdates

def getLogConfigFileUpdatesDict(logConfFile):
    logConfigFileUpdates = {}
    logConfigFileUpdates[logConfFile] = createMD5([logConfFile])[0]
    return logConfigFileUpdates


def hash_bytestr_iter(bytesiter, hasher, ashexstr=False):
    for block in bytesiter:
        hasher.update(block)
    return (hasher.hexdigest() if ashexstr else hasher.digest())

def file_as_blockiter(afile, blocksize=65536):
    with afile:
        block = afile.read(blocksize)
        while len(block) > 0:
            yield block
            block = afile.read(blocksize)


def createMD5(fnamelst):
    return [(fname, hash_bytestr_iter(file_as_blockiter(open(fname, 'rb')), hashlib.md5()))
        for fname in fnamelst]

def main():
    lint = False
    debugmsg = False
    console = False

    global controller
    global theconfigfile
    global dconfdir
    global theloggingfile
    global thepidfile

    defaultattrs={
        #Postfix version 2.1 and later:
        'request':'smtpd_access_policy',
        'protocol_state':'RCPT',
        'protocol_name':'SMTP',
        'helo_name':'smtp.example.com',
        'queue_id':'8045F2AB23',
        'sender':'sender@example.com',
        'recipient':'recipient@example.org',
        'recipient_count':'1',
        'client_address':'1.2.3.4',
        'client_name':'host.example.net',
        'reverse_client_name':'host.example.net',
        'instance':'123.456.7',
        #Postfix version 2.2 and later:
        'sasl_method':'',
        'sasl_username':'',
        'sasl_sender':'',
        'size':'12345',
        'ccert_subject':'',
        'ccert_issuer':'',
        'ccert_fingerprint':'',
        #Postfix version 2.3 and later:
        'encryption_protocol':'TLSv1/SSLv3',
        'encryption_cipher':'DHE-RSA-AES256-SHA',
        'encryption_keysize':'256',
        'etrn_domain':'',
        #Postfix version 2.5 and later:
        'stress':'',
    }

    parser = optparse.OptionParser(version=__version__)
    parser.add_option("--lint", action="store_true", dest="lint",
                    default=False, help="Check configuration and exit")
    parser.add_option("--console", action="store_true", dest="console",
                    default=False, help="start an interactive console after postomaat startup")
    parser.add_option("--attrs", action="store_true", dest="attrs",
                    default=False, help="print available message attributes and their defaults and exit")
    parser.add_option("-f", "--foreground", action="store_true", dest="foreground", default=False,
                    help="start postomaat in the foreground, even if daemonize is enabled in the config")
    parser.add_option("--pidfile", action="store", dest="pidfile",
                    help="use a different pidfile than /var/run/postomaat.pid")
    parser.add_option("-c", "--config", action="store", dest="configfile",
                    help="use a different config file and disable reading from /etc/postomaat/conf.d, logging configuration file is supposed to be in the same directory")
    parser.add_option("--debugmsg", action="store_true", dest="debugmsg",
                    default=False, help="simulate a message and print the result. use the debugmsg arguments listed below to override defaults")
    parser.add_option("--debugmsgport", action="store", dest="debugmsgport",help="test a specific port configuration with debugmsg. if not specified, all plugins are run")
    parser.add_option("--debug", action="store_true", dest="debug",
                    default=False, help="enable verbose/debug output for lint/debugmsg")
    for k,v in iter(defaultattrs.items()):
        parser.add_option("--%s"%k,action="store",dest=k,default=None,help="debugmsg argument, default=%s"%v)

    (opts, args) = parser.parse_args()
    if len(args) > 0:
        print("Unknown option(s): %s" % args)
        print("")
        parser.print_help()
        sys.exit(1)


    lint = opts.lint
    console = opts.console
    debugmsg = opts.debugmsg


    if opts.pidfile:
        thepidfile = opts.pidfile

    if opts.configfile:
        theconfigfile = opts.configfile
        theloggingfile = os.path.join(
            os.path.split(theconfigfile)[0], os.path.split(theloggingfile)[1])
        dconfdir = None

    if opts.attrs:
        print("Supported attributes and their default:")
        print("See http://www.postfix.org/SMTPD_POLICY_README.html#protocol for a full description")
        print("")
        for k,v in iter(defaultattrs.items()):
            print("%s : %s"%(k,v))
        sys.exit(0)


    config=ConfigParser.ConfigParser()
    if not os.path.exists(theconfigfile):
        print("""Configfile (%s) not found. Please create it by renaming the .dist file and modifying it to your needs""" % theconfigfile)
        sys.exit(1)
    with open(theconfigfile) as fp:
        readconfig = config.readfp(fp)
    # load conf.d
    if dconfdir and os.path.isdir(dconfdir):
        filelist = os.listdir(dconfdir)
        configfiles = [dconfdir + '/' + c for c in filelist if c.endswith('.conf')]
        readfiles = config.read(configfiles)


    daemon = DaemonStuff(thepidfile)
    #we could have an empty config file
    if  not (lint or debugmsg or console or opts.foreground):
        if config.has_option('main', 'daemonize'):
            if config.getboolean('main', 'daemonize'):
                daemon.createDaemon()
        else: # option not specified -> default to run daemon
            daemon.createDaemon()

    # drop privileges
    try:
        running_user=config.get('main','user')
        running_group=config.get('main','group')
    except Exception:
        running_user='nobody'
        running_group='nobody'

    priv_drop_ok = False
    try:
        daemon.drop_privs(running_user, running_group)
        priv_drop_ok = True
    except:
        err = sys.exc_info()[1]
        print("Could not drop privileges to %s/%s : %s" %
            (running_user, running_group, str(err)))

    # all threads/processes write to the logQueue which
    # will be handled by a separate process
    logQueue   = multiprocessing.Queue(-1)
    logFactoryQueue = multiprocessing.Queue(-1)


    if lint or debugmsg:
        fc=postomaat.funkyconsole.FunkyConsole()
        print(fc.strcolor("postomaat", "yellow"))
        print(fc.strcolor(__version__, "green"))

        # check if directory used by logfile exists
        if not checkLogfileConfig(theloggingfile):
            sys.exit(1)

        level=logging.INFO
        if opts.debug:
            level=logging.DEBUG
        logConfigure = postomaat.logtools.logConfig(lint=True,lintlevel=level)
    else:
        logConfigure = postomaat.logtools.logConfig(logConfigFile=theloggingfile)

    #--
    # start process handling logging queue
    #--
    logProcessFactory = multiprocessing.Process(target=postomaat.logtools.logFactoryProcess,
                                        args=(logFactoryQueue,logQueue))
    logProcessFactory.start()

    # now create a log - listener process
    logFactoryQueue.put(logConfigure)

    # setup this main thread to send messages to the log queue
    # (which is handled by the logger process created by the logProcessFactory)
    postomaat.logtools.client_configurer(logQueue)

    #===                      ===#
    #= Now logging is available =#
    #===                      ===#
    baselogger = logging.getLogger()
    baselogger.info("postomaat version %s starting up" % __version__)

    #--
    # start try-except-finally statement to make sure logging the logging
    # process is stopped correctly if an exception happens
    #--
    try:
        # Setup address compliance checker
        try:
            address_check = config.get('main','address_compliance_checker')
        except Exception as e:
            # might happen for some tests which do not propagate defaults
            address_check = "Default"
        Addrcheck().set(address_check)

        # instantiate the MainController and load default configuration
        controller = MainController(config,logQueue,logFactoryQueue)
        controller.configFileUpdates = getConfigFileUpdatesDict(theconfigfile,dconfdir)
        controller.logConfigFileUpdates = getLogConfigFileUpdatesDict(theloggingfile)
        controller.propagate_core_defaults()


        if lint:
            controller.lint()
        elif debugmsg:
            attrs=defaultattrs.copy()

            for arg in defaultattrs.keys():
                if getattr(opts,arg) is not None:
                    attrs[arg]=getattr(opts,arg)
            print(attrs)
            action,arg=controller.test(attrs,port=opts.debugmsgport)
            if arg is None:
                arg=""
            print("Result: %s %s"%(action.upper(),arg))
        else:
            signal.signal(signal.SIGHUP, sighup)
            if console:
                controller.debugconsole = True
            controller.startup()
    except Exception as e:
        baselogger.error("Exception catched: %s"%(str(e)))
        #baselogger.exception(e)
    finally:
        #---
        # stop logger factory & process
        #---
        baselogger.info("Stop logging framework -> Goodbye")
        try:
            baselogger.debug("Send Poison pill to logFactoryQueue")
            logFactoryQueue.put_nowait(None)
            logProcessFactory.join(120)
        except Exception as e:
            logProcessFactory.terminate()

Al-HUWAITI Shell