Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Python

FTPoverEmail Beta v1.00

5.00/5 (1 vote)
9 Sep 2014GPL3 5.9K  
FTPoverEmail Beta v1.00

I made a few more edits to FTPoverEmail. I got the SSH functionality working and fixed a couple of bugs for this next version. I’ve started studying for CCDP so my work on it has tailed off a fair amount. Here’s the next version with the bug fixes and SSH functionality. I called this one a beta in that at least it runs fully and that at least it works most of the time now :-D.

Python
# __author__ = 'gecman'
import smtplib
import email
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
import mimetypes
import os
import imaplib
import time
import sys
import subprocess
import signal
import urllib3

# TODO: Windows-1252 encoding adds an = sign which the program can't handle. 
# Find a way to take care of it.
# TODO: Test more rapidly sent email
# TODO: Find a way to break interactive commands
# TODO: Ensure logging for all error messages
# The bash script checks progOverEmail2_new

# Global Variables
from_name = ''
to_name = ''
subject = ''
log_file = None
user = "your_email_here@gmail.com"
password = "your_password_here"

# sendresponse sends text based responses to the client
# response: The response text to send in the body of the mail
def sendresponse(response):
    global log_file

    # Create a response email from the response string
    response_email = MIMEText(response)

    # Configure the message header fields
    response_email['Subject'] = subject
    response_email['From'] = to_name
    response_email['To'] = from_name

    try:

        # Declare an instance of the SMTP class and connect to the smtp server
        smtp = smtplib.SMTP('smtp.gmail.com')

        # Start the TLS connection
        smtp.starttls()

        # Login to the server
        smtp.login(user, password)

        smtp.sendmail(to_name, from_name, response_email.as_string())

        smtp.close()

    except smtplib.SMTPResponseException:
        # The following lines dump the details of the error to stdout and log any issues
        print(
            "Server encountered the following error while attempting to 
             connect to SMTP server: \nType: {0}\nValue: {1}\nTraceback: {2}".format(
                str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2])))
        log_file.write(time.strftime("%c") + "   -------->     " +
            "Server encountered the following error while attempting to 
             connect to SMTP server: \nType: {0}\nValue: {1}\nTraceback: {2} \r\n".format(
                str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2])))


# sendfile Instead of just sending text, sends a file. Primarily used for the FTP get functionality.
# file_name: The name of the file to be attached to the email.
def sendfile(file_name, optional_text=None):
    global log_file

    # Declare the mime class and message header fields
    response = MIMEMultipart()

    response['Subject'] = subject
    response['From'] = to_name
    response['To'] = from_name

    # Check to see if the caller passed optional text to place in the e-mail, 
    # otherwise use the default response.
    if bool(optional_text):

        # Write the optional text into the e-mail instead of the default
        response.attach(MIMEText(optional_text))

    # Use the default response
    else:
        # Attach the text portion of the message. Alerts the user of the file name 
        # and that it is attached.
        response.attach(MIMEText("Grabbed file " + file_name + ". See attachment."))

    # Define the major and minor mime type
    # attachment = MIMEBase('application', "octet-stream")

    # This command uses the mimetypes module to attempt to guess the mimetype of the file. 
    # The strict=False argument
    # tells the mimetypes module to include modules not officially registered with IANA
    # The file_type variable will be in the format of a tuple #major type#/#sub type# , #encoding#
    file_type = mimetypes.guess_type(file_name, strict=False)
    main_type = None

    # mimetypes.guess_type returns None if it doesn't recognize the file type. 
    # In this case I chose to use the generic
    # application octect-stream type, which should be safe.
    if file_type is None:
        attachment = MIMEBase('application', 'octet-stream')
    else:
        main_type = file_type[0].split('/')[0]
        sub_type = file_type[0].split('/')[1]
        # This pulls the major type and sub type and splits them around the
        # '/' character providing the major type in the
        # first argument and the sub type in the second argument
        attachment = MIMEBase(main_type, sub_type)

    # Read the file from disk and set it as the payload for the email
    # The with statement ensures the file is properly closed even if an exception is raised.
    try:
        f = open(file_name, "rb")
        attachment.set_payload(f.read())
        f.close()
    except OSError:
        print(
            "Server encountered the following error while attempting to open 
              the file you asked for: \nType: {0}\nValue: {1}\nTraceback: {2}".format(
                str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2])))
        log_file.write(time.strftime("%c") + "   -------->     " +
            "Server encountered the following error while attempting to open the file 
             you asked for: \nType: {0}\nValue: {1}\nTraceback: {2}\r\n".format(
                str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2])))
        # TODO: Fix the fact that this shows the newlines as \n instead of properly adding newlines
        sendresponse(
            "Server encountered the following error while attempting to 
              open the file you asked for: \nType: {0}\nValue: {1}\nTraceback: {2}".format(
                str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2])))

    # Check to see if the attachment requires encoding
    if main_type == 'application' or main_type == 'audio' or main_type == 'image':
        # Encode the attachment
        encoders.encode_base64(attachment)

    # This sets the header. The output will be in the following format:
    # Content-Disposition: attachment; file_name="the_file_name"
    # The os.path.basename(file_name) portion strips the leading part of the
    # file path and just gives the actual file name
    attachment.add_header('Content-Disposition', 'attachment; 
        file_name="%s"' % os.path.basename(file_name))

    # A ttach the attachment to the response message
    response.attach(attachment)

    try:

        # Declare an instance of the SMTP class and connect to the smtp server
        smtp = smtplib.SMTP('smtp.gmail.com')

        # Start the TLS connection
        smtp.starttls()

        # Login to the server
        smtp.login(user, password)

        # Send the response email
        smtp.sendmail(to_name, from_name, response.as_string())

        # Close the smtp connection
        smtp.close()

    except smtplib.SMTPResponseException:
        # The following lines dump the details of the error to stdout and log any issues
        print(
            "Server encountered the following error while attempting to connect to 
             SMTP server: \nType: {0}\nValue: {1}\nTraceback: {2}".format(
                str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2])))
        log_file.write(time.strftime("%c") + "   -------->     " +
            "Server encountered the following error while attempting to connect to 
             SMTP server: \nType: {0}\nValue: {1}\nTraceback: {2} \r\n".format(
                str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2])))


# Responsible for connecting to the server via IMAP and actually grabbing the e-mail. 
# It then passes the text or
# content of the e-mail to proccommand for processing.
def getemail():
    # Declare as global variables. They are originally defined at the beginning. 
    # Without these the values won't
    # carry over to other functions.
    global from_name
    global to_name
    global subject
    global log_file

    try:

        # Connect to Google's imap server
        # TODO: This needs to be generic
        mail = imaplib.IMAP4_SSL('imap.gmail.com')

        # Login to the imap server
        mail.login(user, password)

        # List all mailbox (folder) names. In gmail these are called labels.
        mail.list()

        # Select the inbox folder
        mail.select("inbox")

        # Grab an ordered list of all mail. The search function returns an ordered list
        # from newest to oldest. Only grab new mail.
        result, data = mail.search(None, "UNSEEN")

        # Check to see if there was new mail or not. If there wasn't return None.
        if not data[0].split():
            return None

        # Data[0] is a list of email IDs in a space separated string.
        id_list = data[0].split()

        # Grab the latest email
        # TODO add support for queuing
        latest_email_id = id_list[-1]

        # Fetch the email body for the given ID
        result, data = mail.fetch(latest_email_id, "(RFC822)")

        raw_email = data[0][1]

        # Grab the body of the email including the raw text, headers, and payloads
        # The following line converts the email from a string into an email message object
        email_message = email.message_from_bytes(raw_email)

        log_file.write(time.strftime("%c") + "   -------->     " + "-----Receiving E-mail-----\r\n")
        print("-----Receiving E-mail-----\r\n")

        if email_message['To'] is not None:
            print(email_message['To'] + "\r\n")
            log_file.write(time.strftime("%c") + "   -------->     " + email_message['To'] + "\r\n")
            to_name = email_message['To']
        else:
            print("Received email with an empty To address. Ignoring the message.\r\n")
            log_file.write(time.strftime("%c") + "   -------->     " + 
            "Received email with an empty To address. Ignoring the message.\r\n")
            return

        if email_message['Return-Path'] is not None:
            print(email_message['Return-Path'])
            log_file.write(time.strftime("%c") + "   -------->     " + 
            email_message['Return-Path'] + "\r\n")
            from_name = email_message['Return-Path']
        else:
            log_file.write(time.strftime("%c") + "   -------->     " + 
            "Received email with an empty Return-Path. Ignoring the message.\r\n")
            print("Received email with an empty Return-Path. Ignoring the message.")
            return

        if email_message['Subject'] is not None:
            # If there is already a Re in the subject don't add it again
            if email_message['Subject'].find("Re:") == -1:
                subject = "Re: " + email_message['Subject']
            else:
                subject = email_message['Subject']
        else:
            subject = ''

        print(subject + "\r\n")
        log_file.write(time.strftime("%c") + "   -------->     " + subject + "\r\n")

        # Check to see if this is a multipart email message. If it isn't just return the text portion.
        if email_message.get_content_maintype() == 'multipart':

            # Used to determine whether a command was found in the email
            found_command = False

            # Walk the various sections of the email.
            # The enumerate function here returns a number for each email part walked through. 
            # IE 0 for the first part in
            # the email, 1 for the next, and so on.
            for index, part in enumerate(email_message.walk()):

                # The multipart section is just a container so we can skip it.
                if part.get_content_maintype() is 'multipart':
                    continue

                # Check to see if we have reached the text body of the message
                if part.get_content_type() == 'text/plain':
                    found_command = True
                    message = part.get_payload()
                    proccommand(message.splitlines(), index, email_message)

                    # This does not need to continue after this run
                    break

            # If a command was found and successfully processed simply return
            if found_command:
                return
            else:
                sendresponse("Error: No command was found in multipart email. Was it there?")
                print("Error: Server encountered a multipart email that did not appear 
                       to have a command in it")

        # Return the text payload
        elif email_message.get_content_maintype() == 'text':
            print(email_message.get_payload())

            # Pass the message to proccommand
            proccommand(email_message.get_payload().splitlines())

        # It wasn't text - do not process the email
        else:
            print("Error processing email in getemail. Encountered unrecognized content type.")
            log_file.write(time.strftime("%c") + "   -------->     " + 
            "Error processing email in getemail. Encountered unrecognized content type.\r\n")
            sendresponse("Error processing email in getemail. Encountered unrecognized content type.")
            return

        log_file.write(time.strftime("%c") + "   -------->     " + 
         "-----End Message Receive-----\r\n\r\n")

    except imaplib.IMAP4.error:
        print(
            "Unable to pull e-mail via IMAP. An error occured: \nType: 
            {0}\nValue: {1}\nTraceback: {2}".format(
                str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2])))
        # TODO: Fix the fact that this shows the newlines as \n instead of properly adding newlines
        log_file.write(time.strftime("%c") + "   -------->     " +
            "Unable to pull e-mail via IMAP. An error occured: \nType: 
             {0}\nValue: {1}\nTraceback: {2}\r\n".format(
                str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2])))


# Processes the command retrieved from the email server
# message: The message from the email server in the form of [type] [command(s)]
# index: This is an index into a multipart email's parts. getemail may have 
# already processed some of them and it is
# unnecessary to reprocess those parts
# email_message: This is an email_message passed from getemail. 
# In terms of FTP, this is used for the put command
def proccommand(message, index=None, email_message=None):
    global log_file

    #found_command = False

    # TODO These are temporary
    command = message[0].split(' ')
    message = message[0]

    log_file.write(time.strftime("%c") + "   -------->     " + "Message: " + message + "\r\n")

    # Used for logging to determine whether a command was successful or not
    errors_occurred = False


    # Process an FTP command
    if "FTP" == command[0].upper():

        # cd command ---------------------------

        # Process the command "cd"
        if command[1].lower() == "cd":
            # Ensure the correct number of arguments was passed"
            if len(command) != 3:
                sendresponse("Error: Incorrect number of arguments for cd")
                errors_occurred = True
            # Make sure the path exists
            elif not os.path.exists(command[2]):
                sendresponse("Error: The path: \"" + command[2] + "\" does not exist")
                errors_occurred = True
            # Process the "cd" command
            else:
                os.chdir(command[2])
                sendresponse("CD completed successfully. Directory is now: " + os.getcwd())

        # dir command ---------------------------

        # Process the command "dir"
        elif command[1].lower() == "dir":

            # Ensure the correct number of arguments was passed"
            if len(command) < 2 or len(command) > 3:
                sendresponse("Error: Incorrect number of arguments for dir")
                errors_occurred = True
            else:
                # If no arguments are passed to dir then list the current directory
                if len(command) == 2:
                    response = ""
                    for file in os.listdir(os.getcwd()):
                        # Contains newline delimited directory listing at end
                        response = response + file + "\n"
                    sendresponse(response)
                # Process the dir command with directory argument
                else:
                    # Make sure the path exists
                    if not os.path.exists(command[2]):
                        sendresponse("Error: The path: \"" + command[2] + "\" does not exist")
                        errors_occurred = True
                    # If the path does exist then list the directory
                    else:
                        response = ""
                        for file in os.listdir(command[2]):
                            # Contains newline delimited directory listing at end
                            response = response + file + "\n"
                        sendresponse(response)

        # pwd command ---------------------------

        # Process the pwd command
        elif command[1].lower() == "pwd":

            # Ensure correct number of arguments was passed
            if len(command) != 2:
                sendresponse("Error: Incorrect number of arguments for pwd")
                errors_occurred = True
            else:
                # Get current directory and respond
                sendresponse(os.getcwd())

        # get command ---------------------------

        # Process the get command
        elif command[1].lower() == "get":
            # TODO Consider any other downsides to this approach?
            # Ensure correct number of arguments was passed. Must be at least 3
            if len(command) < 3:
                sendresponse("Error: Incorrect number of arguments for get")
                errors_occurred = True
            else:
                # Make sure the file exists. Must include everything after the "FTP get" command
                # The syntax " ".join(command[2:]) takes everything from the 
                # third argument on and joins it as one string
                if not os.path.exists(" ".join(command[2:])):
                    sendresponse("Error: The path: \"" + " ".join(command[2:]) + "\" does not exist")
                    errors_occurred = True
                else:
                    sendfile(" ".join(command[2:]))

        # put command ---------------------------
        elif command[1].lower() == "put":

            # Ensure correct number of arguments was passed. Must be at least 3
            if len(command) < 3:
                sendresponse("Error: Incorrect number of arguments for put")
                errors_occurred = True
            else:

                # There may be a better way to do this, but I'm using this to inform 
                # the functionality below whether it
                # should actually execute. This defaults to true, but gets set to False 
                # if the path is determined to be
                # non-valid
                valid_path = True

                # The line below splits command[2] into two pieces. For example say the path is
                # C:\Users\randomUser\Downloads\tryMe.exe. The split command will split this into
                # C:\Users\randomUser\Downloads\ and tryMe.exe. 
                # The [0] index grabs just the path prefix. In this case
                # bool will return true IF there is a path prefix and false if there isn't. 
                # We don't want to check if
                # the path exists if the user didn't provide any path 
                # (because a path of '' will return false. If the
                # user didn't provide a path we want to just dump the file in the 
                # current directory whatever it is.
                # The syntax " ".join(command[2:]) takes everything from the third argument 
                # on and joins it as one string
                if bool(os.path.split(" ".join(command[2:]))[0]):

                    # Make sure the path exists exists
                    if not os.path.exists(os.path.split(" ".join(command[2:]))[0]):
                        valid_path = False
                        sendresponse(
                            "Error: The path: \"" + os.path.split(" ".join(command[2:]))[0] + 
                            "\" does not exist")
                        errors_occurred = True

                # Only execute this if a valid path is supplied (or no path as the case may be)
                if valid_path:

                    # getemail already parsed some of the sections. 
                    # The index comes from the getemail function and
                    # allows this section to skip the parts of the email that were already processed.
                    # email_message.walk() is a generator, but we want to index into it 
                    # so we use the list function.
                    # This turns it into a list, which we are permitted to index into
                    for part in list(email_message.walk())[index:]:

                        # The multipart section is just a container so we can skip it.
                        if part.get_content_maintype() is 'multipart':
                            continue

                        # Grab the file name from the section
                        file_name = part.get_filename()

                        # Make sure the file name actually exists and if it does write the file.
                        if bool(file_name):

                            try:
                                file_descriptor = open(" ".join(command[2:]), 'wb')

                                # Get the payload of the email and write it. 
                                # The decode=True argument basically says if
                                # the payload is encoded, decode it first otherwise, 
                                # it gets returned as is.
                                file_descriptor.write(part.get_payload(decode=True))
                                file_descriptor.close()

                                sendresponse("Successfully processed the put command: " + message)
                            except OSError:
                                errors_occurred = True
                                print(
                                    "Server encountered the following error while attempting to write 
                                    the file you uploaded: \nType: {0}\nValue: {1}\nTraceback: 
                                    {2}".format(
                                        str(sys.exc_info()[0]), str(sys.exc_info()[1]), 
                                        str(sys.exc_info()[2])))
                                log_file.write(time.strftime("%c") + "   -------->     " +
                                    "Server encountered the following error while attempting 
                                     to write the file you uploaded: \nType: {0}\nValue: 
                                     {1}\nTraceback: {2}\r\n".format(
                                        str(sys.exc_info()[0]), str(sys.exc_info()[1]), 
                                        str(sys.exc_info()[2])))
                                # TODO: Fix the fact that this shows the newlines as \n 
                                # instead of properly adding newlines
                                sendresponse(
                                    "Server encountered the following error while attempting to 
                                     write the file you uploaded: \nType: {0}\nValue: 
                                     {1}\nTraceback: {2}".format(
                                        str(sys.exc_info()[0]), str(sys.exc_info()[1]), 
                                        str(sys.exc_info()[2])))
                            # Break out of the for loop. We should only need to write one file.
                            break
        # Delete a file with the "del" FTP command
        # TODO: This requires more thorough testing
        elif command[1].lower() == "del":

            if len(command) == 3:
                try:
                    os.remove(command[2])
                    sendresponse("Successfully deleted file: " + command[2])
                except OSError:
                    print(
                        "Server encountered the following error while attempting to 
                         delete the file: \nType: {0}\nValue: {1}\nTraceback: {2}".format(
                            str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2])))
                    log_file.write(time.strftime("%c") + "   -------->     " +
                        "Server encountered the following error while attempting to 
                         delete the file: \nType: {0}\nValue: {1}\nTraceback: {2}\r\n".format(
                            str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2])))
                    # TODO: Fix the fact that this shows the newlines as \n instead of 
                    # properly adding newlines
                    sendresponse(
                        "Server encountered the following error while attempting to 
                         delete the file: \nType: {0}\nValue: {1}\nTraceback: {2}".format(
                            str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2])))
            else:
                sendresponse(
                    'Error: Incorrect number of arguments sent to the "del" command. 
                     Syntax is "FTP del <filename"')

        # TODO May not be necessary. Added for testing purposes.
        # Kill the server
        elif command[1].lower() == "kill":
            log_file.close()
            sys.exit(0)

        # This is called if the FTP command does not match any correct command.
        else:
            errors_occurred = True
            sendresponse("Error: That command did not match an FTP command.")
            print("Error: That command did not match an FTP command.")
            log_file.write(time.strftime("%c") + "   -------->     " + 
                           "Error: That command did not match an FTP command.")

    # Perform an operation over SSH
    elif command[0].upper() == "SSH":
        # TODO THIS MUST BE TESTED
        # Ensure the user actually passed an argument to the SSH command
        if len(command) < 2:
            errors_occurred = True
            sendresponse("Error: It does not appeared you supplied an argument for your SSH command.")
            print("Error: It does not appear the user supplied an argument with an SSH command.\r\n")
            log_file.write(time.strftime("%c") + "   -------->     " + 
             "Error: It does not appear the user supplied an argument with an SSH command.")
        # This conditional handles the event that the user wants to receive the output 
        # of their command back in binary
        # form.
        elif command[1].upper() == "BINARY":

            # Make sure the user supplied an argument after the BINARY option
            if len(command) > 2:
                # Attempt to run the command passed to the shell. Output is saved by .check_output 
                # as a byte string
                try:
                    command_output = subprocess.check_output(command[2], shell=True)

                    # Write the output of the command to a file then send the file to the user 
                    # as an attachment.
                    try:
                        file_descriptor = open("output.bin", 'wb')

                        # Get the payload of the email and write it. The decode=True argument 
                        # basically says if
                        # the payload is encoded, decode it first otherwise, it gets returned as is.
                        file_descriptor.write(command_output)
                        file_descriptor.close()

                        sendfile("output.bin", "The output of your command \"" + command[2] + 
                                 "\" is attached.")

                        # Remove the file now that it is no longer needed
                        os.remove("output.bin")

                    except OSError:
                        errors_occurred = True
                        print(
                            "Server encountered the following error while attempting to write
                             the output to a file: \nType: {0}\nValue: {1}\nTraceback: {2}".format(
                                str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2])))
                        log_file.write(time.strftime("%c") + "   -------->     " +
                            "Server encountered the following error while attempting to write 
                             the output to a file: \nType: {0}\nValue: {1}\nTraceback: {2}\r\n".format(
                                str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2])))
                        # TODO: Fix the fact that this shows the newlines as \n instead of 
                        # properly adding newlines
                        sendresponse(
                            "Server encountered the following error while attempting to write 
                             the output to a file: \nType: {0}\nValue: {1}\nTraceback: {2}".format(
                                str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2])))

                # If the command fails, report an error
                except subprocess.CalledProcessError:
                    sendresponse(
                        "Server encountered the following error while attempting to process 
                         the command {3}: \nType: {0}\nValue: {1}\nTraceback: {2}".format(
                            str(sys.exc_info()[0]), str(sys.exc_info()[1]), 
                            str(sys.exc_info()[2]), str(command[1])))
                    print(
                        "Server encountered the following error while attempting to process 
                         the command {3}: \nType: {0}\nValue: {1}\nTraceback: {2}".format(
                            str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2]), 
                            str(command[1])))
                    log_file.write(time.strftime("%c") + "   -------->     " +
                        "Server encountered the following error while attempting to process 
                         the command {3}: \nType: {0}\nValue: {1}\nTraceback: {2}\r\n".format(
                            str(sys.exc_info()[0]), str(sys.exc_info()[1]), 
                            str(sys.exc_info()[2]), str(command[1])))

        else:
            # Attempt to run the command passed to the shell. 
            # The below pipes everything into the command_output object.
            # The command[1:] slice is necessary because we split everything based on spaces above. 
            # This combines all of
            # the elements in the array after 1.
            command_output = subprocess.Popen(" ".join(command[1:]),
                                              shell=True,
                                              stdin=subprocess.PIPE,
                                              stderr=subprocess.PIPE,
                                              stdout=subprocess.PIPE)

            output = ""

            # Print the lines from stderr into the response
            for line in command_output.stderr:
                output = output + str(line.rstrip())[2:-1] + '\n'
                command_output.stdout.flush()

            output += '\n\n'

            # Print the lines from stdout into the response
            for line in command_output.stdout:
                output = output + str(line.rstrip())[2:-1] + '\n'
                command_output.stdout.flush()

            sendresponse("Successfully processed command: " + 
            message + "\r\n\r\n" + output)

    # If the protocol or desired action did not match anything...
    else:
        sendresponse("Error: Bad or nonexistent command: " + message)
        print("Server received an email which matched no commands.")
        log_file.write(time.strftime("%c") + "   -------->     " + 
            "Server received an email which matched no commands.\r\n")

    # Used server side for logging purposes
    if errors_occurred:
        print("Failed to process command \"" + message + "\"")
        log_file.write(time.strftime("%c") + "   -------->     " + 
             "Failed to process command \"" + message + "\"\r\n")
    else:
        print("Successfully processed command: " + message)
        log_file.write(time.strftime("%c") + "   -------->     " + 
             "Successfully processed command: " + message + "\r\n")


# Handler for when someone hits control-c while the server is running. 
# We want to perform cleanup before the server
# exits.
def signal_handler(signal, frame):
    print("Server exiting...")
    log_file.write(time.strftime("%c") + "   -------->     
    " + "Server shutting down...\r\n")
    sys.exit(0)

# This is the main section

# Register the signal handler for control-c
signal.signal(signal.SIGINT, signal_handler)

# Open the log file for other functions to use
try:

    # This opens the log file
    log_file = open("log_file", 'a')
except OSError:
    print(
        "Server encountered the following error while attempting to open the 
            log file: \nType: {0}\nValue: {1}\nTraceback: {2}".format(
            str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2])))
    sys.exit(1)

# This is the main loop
while 1:
    getemail()
    time.sleep(5)

log_file.close()

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)