#!/usr/bin/env python3
# Copyright (c) 2009 Arista Networks, Inc.  All rights reserved.
# Arista Networks, Inc. Confidential and Proprietary.

# pylint: disable=consider-using-f-string


import os, sys, optparse # pylint: disable=deprecated-module
import Email, Cell

from email import encoders
# pylint: disable=no-name-in-module
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
# pylint: enable=no-name-in-module

class emailer:
   '''constructs and sends an email with attachments'''

   def __init__( self, emailSettings, netSettings, allVrfStatusLocal, smtpDebug):
      self.emailSettings = emailSettings
      self.netSettings   = netSettings
      self.smtpDebug     = smtpDebug
      self.message       = None
      self.allVrfStatusLocal  = allVrfStatusLocal

   def __attachBinary( self, attachment ):
      ''' Encode the attachment using base64, and add it to the message \
as application/octet-stream. '''
      att = MIMEBase( 'application', 'octet-stream')
      att.set_payload(attachment)
      encoders.encode_base64( att )
      att[ 'Content-Disposition' ] = 'attachment'
      self.message.attach( att )

   def send( self, toList, subject, msgBody, attachments, subtype="plain" ):
      '''message construction and transmission'''
      self.message = MIMEMultipart( 'mixed', None )
      self.message[ 'To' ] = ", ".join( toList )
      self.message[ 'Subject' ] = subject
      textMsg = MIMEText( msgBody, subtype )
      textMsg[ 'Content-Disposition' ] = 'inline'
      self.message.attach( textMsg )
      for attachment in attachments:
         self.__attachBinary( attachment )
      try:
         Email.send_smtp( self.emailSettings, self.netSettings,
                          self.allVrfStatusLocal, self.message,
                          debug=self.smtpDebug )
      except Email.EmailFailure as e:
         print( '%% %s' % e )


class main:
   '''main body of the email program.
   processes command line arguments, reads settings from sysdb,
   and sends a message using the emailer helper.'''

   def __init__( self ):
      self.options = None
      self.toList  = None
      self.emailSettings = None
      self.netSettings   = None
      self.allVrfStatusLocal = None

   def __readConfigFromSysDB( self, sysdbName ):
      import EntityManager # pylint: disable=import-outside-toplevel
      em = EntityManager.Sysdb(sysdbName)
      mg = em.mountGroup()
      self.netSettings   = mg.mount( 'sys/net/config',
                                     'System::NetConfig',
                                     'r' )
      self.emailSettings = mg.mount( 'sys/email/config',
                                     'System::EmailConfig',
                                     'r' )
      self.allVrfStatusLocal = mg.mount( Cell.path( 'ip/vrf/status/local' ),
                                         'Ip::AllVrfStatusLocal',
                                         'r' )
      mg.close( blocking=True )

   def __processCmdLineOptions( self ):
      '''sets up command line option parser and instance variables'''
      op = optparse.OptionParser(
         usage="%prog -- send email through the configured SMTP server" )
      op.add_option( '-a', '--attachment', action='store',
                     help='send the named file as an attachment' )
      op.add_option( '-b', '--binary', action='store_true',
                     help='force encoding attachments as binary' )
      op.add_option( '-d', '--debug', action='store_true',
                     help='debug interaction with SMTP server' )
      op.add_option( '-i', '--interactive', action='store_true',
                     help='force interactive mode even if stdin is not a TTY' )
      op.add_option( '-r', '--ref', action='store',
                     help='specify case ref' )
      op.add_option( '-s', '--subject', action='store',
                     default='Support email sent from the switch',
                     help='specify subject' )
      op.add_option( '-H', '--html', action='store_true',
                     help='send message body as HTML' )
      op.add_option( '--sysname', action='store',
                     default='ar',
                     help='specify Sysdb sysname' )
      self.options, self.toList = op.parse_args( sys.argv[ 1: ] )
      if len( self.toList ) < 1:
         op.error( 'Specify one or more space separated email destinations' )

   def run( self ):
      '''the main body of the program'''

      # gain root privilege so as to enter the right Vrf context
      if os.geteuid() != 0:
         try:
            os.seteuid(0)
            os.setuid(0)
         except OSError:
            try:
               preserve = [ "LD_LIBRARY_PATH", "PATH" ]
               variables = [ "%s=%s" % ( var, os.environ.get( var, "" ) ) \
                     for var in preserve ]
               cmd = ( [ "/usr/bin/sudo", "/usr/bin/env" ] + variables
                  + [ sys.executable ] + sys.argv )
               os.execv( cmd[0], cmd )
            except:
               print( "Failed to switch to root" )
               raise

      self.__processCmdLineOptions()

      if self.options.ref is not None:
         ref = self.options.ref
         if not( ref.startswith( 'ref:' ) ): # pylint: disable=superfluous-parens
            ref = "\nref:" + ref + ":ref\n"
      else:
         ref = ''

      isOnTTY = False 
      if self.options.interactive or sys.stdin.isatty():
         isOnTTY = True
         print( "Enter your message below.  Hit ^D to finish the message." )
      body = sys.stdin.read()

      attachments = []
      # attach the main message depending on options
      if self.options.binary or not isOnTTY:
         message = "see attachment\n" + ref 
         attachments = [ body ]
      else:
         if ref:
            body += "\n\n" + ref
         message = body

      # if a file is also specified, we will send that as an attachment as well
      if ( self.options.attachment is not None and
           os.path.isfile(self.options.attachment) ):
         # pylint: disable-next=consider-using-with
         f = open( self.options.attachment, 'rb' )
         attachment = f.read()
         f.close()
         attachments += [ attachment ]

      self.__readConfigFromSysDB( self.options.sysname )

      subtype = "plain"
      if self.options.html:
         subtype = "html"

      # now we can send it
      mailer = emailer( self.emailSettings, self.netSettings,
                        self.allVrfStatusLocal, self.options.debug )
      mailer.send( self.toList, self.options.subject, message, attachments, subtype )


if __name__ == '__main__':
   pgm = main()
   pgm.run()
