'''
Report interscanners for sagator

(c) 2003-2008 Jan ONDREJ (SAL) <ondrejj(at)salstar.sk>

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.
    
'''

from avlib import *
import re, socket, urllib
from match import match_any

__all__=['report', 'report_recipients']

#####################################################################
### REPORT class

# MESSAGE REPORT TEMPLATE
REPORT_MSG_TEMPLATE='''\
From: %(FROM)s
To: %(TO)s
Subject: ALERT, %(VIRNAME)s FROM %(SENTBY_IP)s!

                             VIRUS FOUND!
---------------------------------------------------------------------
STATUS: %(STATUS)s
QUARANTINED AS: %(QNAME)s
---------------------------------------------------------------------
SMTP COMMUNICATION:
%(SMTP_COMM)s
---------------------------------------------------------------------
SCANNER: %(SCANNER_NAME)s
%(SCANNER_OUTPUT)s
---------------------------------------------------------------------
HEADER OF MESSAGE:
%(MSG_HEADER)s
'''

class report(match_any):
  '''
  Report any message to admin, user or anybody.

  This module can be used to report an message to administrator
  or to any user. You can use following variables in report messages:
    %(FROM)s		report sender
    %(VIRNAME)s		detected virus name
    %(SMTP_COMM)s	SMTP communication
    %(STATUS)s		status of this message (REJECTED, DROPPED, ...)
    %(QNAME)s		filename in quarantine
    %(QQNAME)s		quoted (urlencoded) filename in quarantine
    %(SCANNER_OUTPUT)s	returned scanner output
    %(SCANNER_NAME)s	name of the scanner
    %(MSG_HEADER)s	virus/spam header
    %(MSG_BODY)s	virus/spam body (without header)
    %(SUBJECT)s		message's header Subject
    %(RANDOM)s		10 random characters (useable to generate a boundary)
    %(SENDER)s		virus/spam sender
    %(RECIPIENTS)s	virus/spam recipients joined by comma (',')
    %(TO)s		report recipeints
    %(VERSION)s		sagator's version
    %(SENTBY_IP)s	sender's IP
    %(SENTBY_NAME)s	sender's hostname
    %(SENTBY_HELO)s	sender's HELO/EHLO string
  
  Usage: report(recipients, msg, scanners)
     or: report(...).sender('<email@of.sender.dom>')
     or: report(...).ifscan(cond_scanner)

  Where: recipients is an array of recipients,
           like: ['<r1@dom>','<r2@dom>',...]
           You can use variables mentioned above in this array, but only
           %(SENDER)s can be used logically.
         msg is an message template, you can use report.MSG_TMPL
         cond_scanner is a scanner, which defines, when report may be sent

  Examples:
    - send report to admin@localhost:
        report(['<admin@localhost>'], report.MSG_TMPL, ... )
    - send report to email sender:
        report(['%(SENDER)s'], report.MSG_TMPL, ... )
  '''
  # Templates
  MSG_TMPL=REPORT_MSG_TEMPLATE
  # scanner
  name='report()'
  def __init__(self,recipients,msg,*scanners):
      self.MSG=msg
      self.RECIPIENTS=recipients
      self.SENDER='<vscan@'+socket.gethostname()+'>'
      match_any.__init__(self,scanners)
  def sender(self,email):
      self.SENDER=email
      return self
  def ifscan(self,scanner):
      self.IFSCAN=scanner
      return self
  def ifscan_main(self,buffer,args={}):
      try:
        level,detected,virlist=self.IFSCAN.scanbuffer(buffer,args)
      except (NameError,AttributeError):
        return True
      if is_infected(level,detected):
        debug.echo(6,'report(): condition: True (%f,%s)' % (level,detected))
        return True
      else:
        debug.echo(6,'report(): condition: False (%f,%s)' % (level,detected))
        return False
  def scanbuffer(self,buffer,args={}):
      level,detected,virlist=match_any.scanbuffer(self,buffer,args)
      if globals.scan_only:
        return level,detected,virlist
      if not self.ifscan_main(buffer,args):
        return level,detected,virlist
      if not is_infected(level,detected):
        return level,detected,virlist
      if self.RECIPIENTS:
        sender=mail.getsender()
        repl_vars={
          'FROM':		self.SENDER,
          'VIRNAME':		detected,
          'SMTP_COMM':		mail.noop_connect_from()+mail.comm.rstrip(),
          'STATUS':		globals.action(level),
          'QNAME':		globals.QFNAME,
          'QQNAME':		urllib.quote_plus(globals.QFNAME),
          'SCANNER_OUTPUT':	"".join(virlist),
          'SCANNER_NAME':	globals.found_by.name,
          'MSG_HEADER':		mail.data[:mail.bodypos].rstrip(),
          'MSG_BODY':		mail.data[mail.bodypos:],
          'SUBJECT':		mail.headers.get('Subject'),
          'RANDOM':		randomchars(10),
          'SENDER':             mail.sender,
          'RECIPIENTS':         ','.join(mail.recip),
          'VERSION':		SG_VER_REL,
          'SENTBY_IP':		sender['ADDR'],
          'SENTBY_NAME':	sender['NAME'],
          'SENTBY_HELO':	sender['HELO']
        }
        recipients=[replace_tmpl(x,repl_vars) for x in self.RECIPIENTS]
        repl_vars['TO']=',\r\n\t'.join(recipients)
        msg=replace_tmpl(self.MSG, repl_vars)
        try:
          smtpc().sendmail(self.SENDER, recipients, msg)
        except SmtpcError,(ses,es):
          debug.echo(1,"report(): AdminReport: ", ses)
      return level,detected,virlist

class report_recipients(report):
  '''
  Report any message to email recipients.

  This module can be used to report an message to mail recipients.
  For message description see report() scanner.
 
  Usage: report_recipients(msg, scanners)
     or: report_recipients(...).sender('<email@of.sender.dom>')
     or: report(...).ifscan(cond_scanner)

  Where: msg is an message template, you can use report.MSG_TMPL
          scanners are scanners which are used
         cond_scanner is a scanner, which defines, when report may be sent
  '''
  # Templates
  MSG_TMPL=REPORT_MSG_TEMPLATE
  # scanner
  name='report_recipients()'
  def __init__(self,msg,*scanners):
      self.MSG=msg
      self.SENDER='<vscan@'+socket.gethostname()+'>'
      match_any.__init__(self,scanners)
  def scanbuffer(self,buffer,args={}):
      level,detected,virlist=match_any.scanbuffer(self,buffer,args)
      if globals.scan_only:
        return level,detected,virlist
      if not self.ifscan_main(buffer,args):
        return level,detected,virlist
      if not is_infected(level,detected):
        return level,detected,virlist
      sender=mail.getsender()
      repl_vars={
          'FROM':		self.SENDER,
          'VIRNAME':		detected,
          'SMTP_COMM':		mail.noop_connect_from()+mail.comm.rstrip(),
          'STATUS':		globals.action(level),
          'QNAME':		globals.QFNAME,
          'QQNAME':		urllib.quote_plus(globals.QFNAME),
          'SCANNER_OUTPUT':	"".join(virlist),
          'SCANNER_NAME':	globals.found_by.name,
          'MSG_HEADER':		mail.data[:mail.bodypos].rstrip(),
          'MSG_BODY':		mail.data[mail.bodypos:],
          'RANDOM':		randomchars(10),
          'SENDER':             mail.sender,
          'RECIPIENTS':         ','.join(mail.recip),
          'TO':			',\r\n\t'.join(mail.recip),
          'VERSION':		SG_VER_REL,
          'SENTBY_IP':		sender['ADDR'],
          'SENTBY_NAME':	sender['NAME'],
          'SENTBY_HELO':	sender['HELO']
      }
      msg=replace_tmpl(self.MSG,repl_vars)
      try:
        smtpc().sendmail(self.SENDER,mail.recip,msg)
      except SmtpcError,(ses,es):
        debug.echo(1,"report(): UserReportRecipients: ",ses)
      return level,detected,virlist
