HEX
Server: LiteSpeed
System: Linux php-prod-1.spaceapp.ru 5.15.0-157-generic #167-Ubuntu SMP Wed Sep 17 21:35:53 UTC 2025 x86_64
User: xnsbb3110 (1041)
PHP: 8.1.33
Disabled: NONE
Upload Files
File: //usr/local/CyberCP/emailMarketing/emailMarketing.py
#!/usr/local/CyberCP/bin/python

import os
import time
import csv
import re
import plogical.CyberCPLogFileWriter as logging
from .models import EmailLists, EmailsInList, EmailTemplate, EmailJobs, SMTPHosts, ValidationLog
from plogical.backupSchedule import backupSchedule
from websiteFunctions.models import Websites
import threading as multi
import socket, smtplib
import DNS
from random import randint
from plogical.processUtilities import ProcessUtilities

class emailMarketing(multi.Thread):
    def __init__(self, function, extraArgs):
        multi.Thread.__init__(self)
        self.function = function
        self.extraArgs = extraArgs

    def run(self):
        try:
            if self.function == 'createEmailList':
                self.createEmailList()
            elif self.function == 'verificationJob':
                self.verificationJob()
            elif self.function == 'startEmailJob':
                self.startEmailJob()
        except BaseException as msg:
            logging.CyberCPLogFileWriter.writeToFile(str(msg) + ' [emailMarketing.run]')

    def createEmailList(self):
        try:
            website = Websites.objects.get(domain=self.extraArgs['domain'])
            try:
                newList = EmailLists(owner=website, listName=self.extraArgs['listName'], dateCreated=time.strftime("%I-%M-%S-%a-%b-%Y"))
                newList.save()
            except:
                newList = EmailLists.objects.get(listName=self.extraArgs['listName'])

            counter = 0

            if self.extraArgs['path'].endswith('.csv'):
                with open(self.extraArgs['path'], 'r') as emailsList:
                    data = csv.reader(emailsList, delimiter=',')
                    for items in data:
                        try:
                            for value in items:
                                if re.match('^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$', value) != None:
                                    try:
                                        getEmail = EmailsInList.objects.get(owner=newList, email=value)
                                    except:
                                        try:
                                            newEmail = EmailsInList(owner=newList, email=value,
                                                                    verificationStatus='NOT CHECKED',
                                                                    dateCreated=time.strftime("%I-%M-%S-%a-%b-%Y"))
                                            newEmail.save()
                                        except:
                                            pass
                                    logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], str(counter) + ' emails read.')
                                    counter = counter + 1
                        except BaseException as msg:
                            logging.CyberCPLogFileWriter.writeToFile('%s. [createEmailList]' % (str(msg)))
                            continue
            elif self.extraArgs['path'].endswith('.txt'):
                with open(self.extraArgs['path'], 'r') as emailsList:
                    emails = emailsList.readline()
                    while emails:
                        email = emails.strip('\n')
                        if re.match('^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$', email) != None:
                            try:
                                getEmail = EmailsInList.objects.get(owner=newList, email=email)
                            except BaseException as msg:
                                newEmail = EmailsInList(owner=newList, email=email, verificationStatus='NOT CHECKED',
                                                        dateCreated=time.strftime("%I-%M-%S-%a-%b-%Y"))
                                newEmail.save()
                            logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],str(counter) + ' emails read.')
                            counter = counter + 1
                        emails = emailsList.readline()

            logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], str(counter) + 'Successfully read all emails. [200]')
        except BaseException as msg:
            logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], str(msg) +'. [404]')
            return 0

    def findNextIP(self):
        try:
            if self.delayData['rotation'] == 'Disable':
                return None
            elif self.delayData['rotation'] == 'IPv4':
                if self.delayData['ipv4'].find(',') == -1:
                    return self.delayData['ipv4']
                else:
                    ipv4s = self.delayData['ipv4'].split(',')

                    if self.currentIP == '':
                        return ipv4s[0]
                    else:
                        returnCheck = 0

                        for items in ipv4s:
                            if returnCheck == 1:
                                return items
                            if items == self.currentIP:
                                returnCheck = 1

                        return ipv4s[0]
            else:
                if self.delayData['ipv6'].find(',') == -1:
                    return self.delayData['ipv6']
                else:
                    ipv6 = self.delayData['ipv6'].split(',')

                    if self.currentIP == '':
                        return ipv6[0]
                    else:
                        returnCheck = 0

                        for items in ipv6:
                            if returnCheck == 1:
                                return items
                            if items == self.currentIP:
                                returnCheck = 1
                    return ipv6[0]
        except BaseException as msg:
            logging.CyberCPLogFileWriter.writeToFile(str(msg))
            return None

    def verificationJob(self):
        try:

            verificationList = EmailLists.objects.get(listName=self.extraArgs['listName'])
            domain = verificationList.owner.domain

            if not os.path.exists('/home/cyberpanel/' + domain):
                os.mkdir('/home/cyberpanel/' + domain)

            tempStatusPath = '/home/cyberpanel/' + domain + "/" + self.extraArgs['listName']
            logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, 'Starting verification job..')

            counter = 1
            counterGlobal = 0

            allEmailsInList = verificationList.emailsinlist_set.all()

            configureVerifyPath = '/home/cyberpanel/configureVerify'
            finalPath = '%s/%s' % (configureVerifyPath, domain)


            import json
            if os.path.exists(finalPath):
                self.delayData = json.loads(open(finalPath, 'r').read())

            self.currentIP = ''

            ValidationLog(owner=verificationList, status=backupSchedule.INFO, message='Starting email verification..').save()

            for items in allEmailsInList:
                if items.verificationStatus != 'Verified':
                    try:

                        email = items.email
                        self.currentEmail = email
                        domainName = email.split('@')[1]
                        records = DNS.dnslookup(domainName, 'MX', 15)

                        counterGlobal = counterGlobal + 1

                        for mxRecord in records:

                            # Get local server hostname
                            host = socket.gethostname()

                            ## Only fetching smtp object

                            if os.path.exists(finalPath):
                                try:
                                    delay = self.delayData['delay']
                                    if delay == 'Enable':
                                        if counterGlobal == int(self.delayData['delayAfter']):
                                            ValidationLog(owner=verificationList, status=backupSchedule.INFO,
                                                          message='Sleeping for %s seconds...' % (self.delayData['delayTime'])).save()

                                            time.sleep(int(self.delayData['delayTime']))
                                            counterGlobal = 0
                                            self.currentIP = self.findNextIP()

                                            ValidationLog(owner=verificationList, status=backupSchedule.INFO,
                                                          message='IP being used for validation until next sleep: %s.' % (str(self.currentIP))).save()

                                            if self.currentIP == None:
                                                server = smtplib.SMTP(timeout=10)
                                            else:
                                                server = smtplib.SMTP(self.currentIP, timeout=10)
                                        else:

                                            if self.currentIP == '':
                                                self.currentIP = self.findNextIP()
                                                ValidationLog(owner=verificationList, status=backupSchedule.INFO,
                                                              message='IP being used for validation until next sleep: %s.' % (
                                                                  str(self.currentIP))).save()

                                            if self.currentIP == None:
                                                server = smtplib.SMTP(timeout=10)
                                            else:
                                                server = smtplib.SMTP(self.currentIP, timeout=10)
                                    else:
                                        logging.CyberCPLogFileWriter.writeToFile(
                                            'Delay not configured..')

                                        ValidationLog(owner=verificationList, status=backupSchedule.INFO,
                                                      message='Delay not configured..').save()

                                        server = smtplib.SMTP(timeout=10)
                                except BaseException as msg:

                                    ValidationLog(owner=verificationList, status=backupSchedule.ERROR,
                                                  message='Delay not configured. Error message: %s' % (str(msg))).save()

                                    server = smtplib.SMTP(timeout=10)
                            else:
                                server = smtplib.SMTP(timeout=10)

                            ###

                            server.set_debuglevel(0)

                            # SMTP Conversation
                            server.connect(mxRecord[1])
                            server.helo(host)
                            server.mail('host' + "@" + host)
                            code, message = server.rcpt(str(email))
                            server.quit()

                            # Assume 250 as Success
                            if code == 250:
                                items.verificationStatus = 'Verified'
                                items.save()
                                break
                            else:
                                ValidationLog(owner=verificationList, status=backupSchedule.ERROR,
                                              message='Failed to verify %s. Error message %s' % (email, message.decode())).save()
                                items.verificationStatus = 'Verification Failed'
                                items.save()

                        logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, str(counter) + ' emails verified so far..')
                        counter = counter + 1
                    except BaseException as msg:
                        items.verificationStatus = 'Verification Failed'
                        items.save()
                        counter = counter + 1
                        ValidationLog(owner=verificationList, status=backupSchedule.ERROR,
                                      message='Failed to verify %s. Error message %s' % (
                                      self.currentEmail , str(msg))).save()


                verificationList.notVerified = verificationList.emailsinlist_set.filter(verificationStatus='Verification Failed').count()
                verificationList.verified = verificationList.emailsinlist_set.filter(verificationStatus='Verified').count()
                verificationList.save()

            ValidationLog(owner=verificationList, status=backupSchedule.ERROR, message=str(counter) + ' emails successfully verified. [200]').save()

            logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, str(counter) + ' emails successfully verified. [200]')
        except BaseException as msg:
            verificationList = EmailLists.objects.get(listName=self.extraArgs['listName'])
            domain = verificationList.owner.domain
            tempStatusPath = '/home/cyberpanel/' + domain + "/" + self.extraArgs['listName']
            logging.CyberCPLogFileWriter.statusWriter(tempStatusPath, str(msg) +'. [404]')
            logging.CyberCPLogFileWriter.writeToFile(str(msg))
            return 0

    def setupSMTPConnection(self):
        try:
            if self.extraArgs['host'] == 'localhost':
                self.smtpServer = smtplib.SMTP('127.0.0.1')
                return 1
            else:
                self.verifyHost = SMTPHosts.objects.get(host=self.extraArgs['host'])
                self.smtpServer = smtplib.SMTP(str(self.verifyHost.host), int(self.verifyHost.port))

                if int(self.verifyHost.port) == 587:
                    self.smtpServer.starttls()

                self.smtpServer.login(str(self.verifyHost.userName), str(self.verifyHost.password))
                return 1
        except smtplib.SMTPHeloError:
            logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
                                                      'The server didnt reply properly to the HELO greeting.')
            return 0
        except smtplib.SMTPAuthenticationError:
            logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
                                                      'Username and password combination not accepted.')
            return 0
        except smtplib.SMTPException:
            logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
                                                      'No suitable authentication method was found.')
            return 0

    def startEmailJob(self):
        try:

            if self.setupSMTPConnection() == 0:
                logging.CyberCPLogFileWriter.writeToFile('SMTP Connection failed. [301]')
                return 0

            emailList = EmailLists.objects.get(listName=self.extraArgs['listName'])
            allEmails = emailList.emailsinlist_set.all()
            emailMessage = EmailTemplate.objects.get(name=self.extraArgs['selectedTemplate'])

            totalEmails = allEmails.count()
            sent = 0
            failed = 0

            ipFile = "/etc/cyberpanel/machineIP"
            f = open(ipFile)
            ipData = f.read()
            ipAddress = ipData.split('\n', 1)[0]

            ## Compose Message
            from email.mime.multipart import MIMEMultipart
            from email.mime.text import MIMEText
            import re

            tempPath = "/home/cyberpanel/" + str(randint(1000, 9999))

            emailJob = EmailJobs(owner=emailMessage, date=time.strftime("%I-%M-%S-%a-%b-%Y"),
                                 host=self.extraArgs['host'], totalEmails=totalEmails,
                                 sent=sent, failed=failed
                                 )
            emailJob.save()

            for items in allEmails:
                try:
                    message = MIMEMultipart('alternative')
                    message['Subject'] = emailMessage.subject
                    message['From'] = emailMessage.fromEmail
                    message['reply-to'] = emailMessage.replyTo

                    if (items.verificationStatus == 'Verified' or self.extraArgs[
                        'verificationCheck']) and not items.verificationStatus == 'REMOVED':
                        try:
                            port = ProcessUtilities.fetchCurrentPort()
                            removalLink = "https:\/\/" + ipAddress + ":%s\/emailMarketing\/remove\/" % (port) + self.extraArgs[
                                'listName'] + "\/" + items.email
                            messageText = emailMessage.emailMessage.encode('utf-8', 'replace')
                            message['To'] = items.email

                            if re.search(b'<html', messageText, re.IGNORECASE) and re.search(b'<body', messageText,
                                                                                             re.IGNORECASE):
                                finalMessage = messageText.decode()

                                self.extraArgs['unsubscribeCheck'] = 0
                                if self.extraArgs['unsubscribeCheck']:
                                    messageFile = open(tempPath, 'w')
                                    messageFile.write(finalMessage)
                                    messageFile.close()

                                    command = "sudo sed -i 's/{{ unsubscribeCheck }}/" + removalLink + "/g' " + tempPath
                                    ProcessUtilities.executioner(command, 'cyberpanel')

                                    messageFile = open(tempPath, 'r')
                                    finalMessage = messageFile.read()
                                    messageFile.close()

                                html = MIMEText(finalMessage, 'html')
                                message.attach(html)

                            else:
                                finalMessage = messageText

                                if self.extraArgs['unsubscribeCheck']:
                                    finalMessage = finalMessage.replace('{{ unsubscribeCheck }}', removalLink)

                                html = MIMEText(finalMessage, 'plain')
                                message.attach(html)

                            try:
                                status = self.smtpServer.noop()[0]
                                self.smtpServer.sendmail(message['From'], items.email, message.as_string())
                            except:  # smtplib.SMTPServerDisconnected
                                if self.setupSMTPConnection() == 0:
                                    logging.CyberCPLogFileWriter.writeToFile('SMTP Connection failed. [301]')
                                    return 0
                                self.smtpServer.sendmail(message['From'], items.email, message.as_string())


                            sent = sent + 1
                            emailJob.sent = sent
                            emailJob.save()
                            logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
                                                                      'Successfully sent: ' + str(
                                                                          sent) + ' Failed: ' + str(
                                                                          failed))
                        except BaseException as msg:
                            failed = failed + 1
                            emailJob.failed = failed
                            emailJob.save()
                            logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
                                                                      'Successfully sent: ' + str(
                                                                          sent) + ', Failed: ' + str(failed))
                            if self.setupSMTPConnection() == 0:
                                logging.CyberCPLogFileWriter.writeToFile(
                                    'SMTP Connection failed. Error: %s. [392]' % (str(msg)))
                                return 0
                except BaseException as msg:
                            failed = failed + 1
                            emailJob.failed = failed
                            emailJob.save()
                            logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
                                                                      'Successfully sent: ' + str(
                                                                          sent) + ', Failed: ' + str(failed))
                            if self.setupSMTPConnection() == 0:
                                logging.CyberCPLogFileWriter.writeToFile('SMTP Connection failed. Error: %s. [399]' % (str(msg)))
                                return 0


            emailJob.sent = sent
            emailJob.failed = failed
            emailJob.save()

            logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'],
                                                      'Email job completed. [200]')
        except BaseException as msg:
            logging.CyberCPLogFileWriter.statusWriter(self.extraArgs['tempStatusPath'], str(msg) + '. [404]')
            return 0