#!/usr/bin/python3

'''
scand.py
(c) 2006-2018 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.

'''

import sys, os, socket, signal, atexit, traceback
import avlib
import aglib
from etc import *

def rmpid():
    try:
      os.unlink(BINDTO)
      os.unlink(SCAND.PID_FILE)
    except:
      #debug.echo(1,"scand: %s" % sys.exc_info()[1])
      pass

def sigterm(sn,stack):
    global EXITING
    EXITING=True

def sighup(sn,stack):
    # reopen logfile
    debug.reopen()

def mainloop(s,scanner):
    while True:
        if EXITING:
            break
        try:
            conn,addr=s.accept()
            conn.settimeout(120)
            f=conn.makefile('rw')
            mail.__init__()
            while True:
                cmd=f.readline().strip('\r\n').split(" ",1)
                debug.echo(5,"scand: %s" % cmd)
                if cmd[0]=='MAIL_FROM':
                    mail.sender=cmd[1]
                elif cmd[0]=='RCPT_TO':
                    mail.recip.append(cmd[1])
                elif cmd[0]=='SCANFILE':
                    try:
                        level, detected, virlist = scanner.scanfile([cmd[1]])
                        conn.sendall('%f %s\n' % (level,detected))
                        for line in virlist:
                            conn.sendall(line)
                    except:
                        conn.sendall('ERROR %s\n' % sys.exc_info()[1])
                    break
                elif cmd[0]=='QUIT':
                    conn.sendall('OK Bye\n')
                    break
                else:
                    conn.sendall('ERROR Illegal command!\n')
                    break
            f.close()
            conn.shutdown(2)
            conn.close()
        except KeyboardInterrupt:
          break
        except SystemExit:
          break
        except socket.error as eces:
          if eces[0]==4:
            continue
          debug.echo(1,"scand: %s" % eces)
          debug.traceback(4,"scand")
        except:
          debug.echo(1,"scand: ERROR: %s" % sys.exc_info()[1])
          debug.traceback(1,"scand")

if __name__ == '__main__':
    EXITING=False
    SCAND=aglib.find_service(SRV,'scand()')
    BINDTO=CHROOT+SCAND.SOCK
    debug.set_level(DEBUG_LEVEL)
    debug.set_logfile(LOGFILE)
    try:
      pidfile=open(SCAND.PID_FILE,'w')
    except IOError as err:
      (ec,es) = err.args
      debug.echo(2,"WARNING: Can't open pidfile for writing: %s" % es)
      pidfile=''
    if ('--as-root' not in sys.argv):
      avlib.globals.setuidgid(USER,GROUP)
    avlib.safe.ROOT_PATH=CHROOT # simulate chroot access
    os.umask(0)
    s=socket.socket(socket.AF_UNIX,socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    try:
      s.bind(BINDTO)
    except socket.error as err:
      (ec,es) = err.args
      if ec==98: # Address already in use
        try:
          s.connect(BINDTO)
          s.sendall('QUIT\n')
          s.close()
          debug.echo(1,"scand: There is another daemon already running."
                       " Exiting now ...")
          sys.exit(0)
        except socket.error:
          debug.echo(1,"scand: Unlinking dead socket file.")
          os.unlink(BINDTO)
          s=socket.socket(socket.AF_UNIX,socket.SOCK_STREAM)
          s.bind(BINDTO)
      else:
        raise
    s.listen(5)
    try:
      os.chown(BINDTO,avlib.globals.UID,avlib.globals.GID)
    except OSError as err:
      (ec,es) = err.args
      debug.echo(2,"WARNING: Can't change socket owner: %s" % es)
    if ('--nodaemon' in sys.argv) or (os.fork()==0):
      if pidfile:
        pidfile.write(str(os.getpid()))
        pidfile.close()
      os.close(0) # close stdin
      signal.signal(signal.SIGTERM, sigterm)
      signal.signal(signal.SIGHUP, sighup)
      signal.signal(signal.SIGUSR2, aglib.sigusr2)
      atexit.register(rmpid)
      debug.echo(1,"scand: daemon started ...")
      mainloop(s,SCAND.SCANNER)
      debug.echo(1,"scand: Exiting ...")
