'''
SAGATOR's web service main index file

(c) 2005 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.
    
'''

__pychecker__ = 'unusednames=_,__auth_realm__ \
                 blacklist=StringIO \
                 maxreturns=20 maxlines=300 \
                 no-local no-argsused no-stringiter no-abstract no-isliteral \
                 no-stdlib no-callinit'

import sys,os,rfc822,crypt,html
from avlib import *
from avlib import _
from aglib import do_scan,send_qfile,find_service
from StringIO import StringIO
try:
  from config import *
except ImportError,es:
  if str(es)[-6:]=="config":
    print "ERROR! Config file not found! Exiting now."
    sys.exit(1)
  else:
    raise

__auth_realm__ = 'Access to SAGATOR (click CANCEL to get a new account)' 
LOGIN='Anonymous'
USER='Anonymous'
SHOWROWS=50
safe.ROOT_PATH=CHROOT
debug.set_logfile(LOGFILE,silent=True)
debug.set_level(DEBUG_LEVEL)
WEBQ=find_service(SRV,'webq_apache()')
DB=WEBQ.DB

HEADER='''
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=%s"/>
<title></title>
<style type="text/css" media="all">
body {
  font-family: "Arial", "Luxi Sans", "Helvetica", sans-serif;
}
table {
  border-style: solid;
  border-width: 1pt;
  border-color: black;
  padding: 5pt;
  width: 100%%;
  margin: auto;
}
th {
  border-style: solid;
  border-width: 0pt 0pt 1pt 0pt;
}
td {
  padding: 1pt 5pt;
  white-space: nowrap;
  vertical-align: top;
}
table.container {
  border-style: none;
  width: 100%%;
  padding: 0pt;
  margin: 0pt;
}
.maxibutton {
  /* display: inline; */
  border-color: #000000;
  background-color: #FF0000;
  color: white;
  font-size: 16pt;
  text-decoration: none;
  border-style: solid;
  border-width: 1pt;
  padding: 0pt 8pt;
  cursor: pointer;
  -moz-border-radius: 8pt;
}
.submit {
  /* display: inline; */
  border-color: #000000;
  background-color: #DF0000;
  color: white;
  font-size: 12pt;
  text-decoration: none;
  border-style: solid;
  border-width: 1pt;
  padding: 0pt 8pt;
  cursor: pointer;
  -moz-border-radius: 5pt;
}
.submit:hover {
  color: inherit;
  background-color: #FF1010;
}
</style>
</head>
<body>
<table class="container">
<tr><td rowspan="4">
  <h2><a href="index" class="submit" style="font-size: 16pt">SAGATOR</a> - %s %s</h2>
</td></tr>
<tr><td style="text-align: right"><a href="webscan" class="submit">%s</a></td></tr>
<tr><td style="text-align: right"><a href="settings" class="submit">%s</a></td></tr>
<tr><td style="text-align: right"><a href="search" class="submit">%s</a></td></tr>
</table>
'''

SEARCH_FORM=_('''\
<form action="index">
<table style="width: 50%%">
%s<tr><td>Sender:</td><td><input type="text" name="sender"/></td></tr>
<tr><td>Virus name:</td><td><input type="text" name="vname"/></td></tr>
<tr><td>Date begins:</td><td><input type="text" name="begin"/></td></tr>
<tr><td>Date ends:</td><td><input type="text" name="end"/></td></tr>
<tr><td colspan="2" style="text-align: center"><input type="submit" value="Search"/></td></tr>
<tr><td colspan="2"><hr/>
You can use exclamation mark ('!') at beginning of sender or virus name,<br/>
if you want to negotiate this expression. For example if you want<br/>
find all viruses (not spams), you can use: Sender: !SPAM
</td></tr>
</table>
</form>
''')

WRONG_PARAMS=_('''\
<h1>Wrong usage!</h1>
You must select at least one email for this operation.
Please go back, select at least one email and then repeat this action again.
''')

SETTINGS=_('''\
<form action="settings" method="post">
<table style="width: 50%%">
<tr><td colspan="2" style="text-align: center"><h1 style="text-align: center">User settings</h1></td></tr>
%s<tr><td colspan="2"><hr/></td></tr>
<tr><td colspan="2" style="text-align: center"><h3 style="text-align: center">Password change</h3></td></tr>
<tr><td style="text-align: right">Old password:</td><td><input type="password" name="old"/></td></tr>
<tr><td style="text-align: right">New password:</td><td><input type="password" name="new"/></td></tr>
<tr><td style="text-align: right">Retype new password:</td><td><input type="password" name="retyped"/></td></tr>
<tr><td colspan="2"><hr/></td></tr>
<tr><td colspan="2" style="text-align: center"><h3 style="text-align: center">Other settings</h3></td></tr>
<tr><td style="text-align: right">Language:</td><td>%s</td></tr>
<tr><td style="text-align: right">Show rows:</td><td><input type="text" name="rows" size="3" value="%s"/></td></tr>
<tr><td colspan="2"><hr/></td></tr>
<tr><td colspan="2" style="text-align: center"><input type="submit" value="Save settings" class="submit"/></td></tr>
</table>
</form>
''')

def _check_pass(user,passwd):
    '''
    Check current login and password pair against current database connection.
    Return True or False.
    '''
    global SHOWROWS
    q=DB.query("SELECT pass,lang,showrows FROM webaccess WHERE email='%s'" % DB.quote(user))
    debug.echo(9,"Password check: %s:%s" % (user,passwd))
    try:
      if q[0][0]==crypt.crypt(passwd,q[0][0]):
        _.set_lang(q[0][1],LOCALE_DIR)
        SHOWROWS=int(q[0][2])
        return True
    except IndexError:
      return False
    except:
      debug.traceback(1,"webaccess")
    return False

def __auth__(req,user,passwd):
    '''
    Publisher authentification method.
    '''
    global LOGIN,USER
    page=os.path.basename(req.canonical_filename)
    debug.echo(4,"Accessing page %s ['%s']" % (req.canonical_filename,page))
    if page=='reg':
      LOGIN='Anonymous'
      USER='Anonymous'
      return True
    LOGIN=user
    USER=re.compile(WEBQ.USERCONV[0]).sub(WEBQ.USERCONV[1],user)
    return _check_pass(user,passwd)

def __access__(req,user):
    '''
    Publisher access method.
    '''
    if os.path.basename(req.canonical_filename) in WEBQ.PERMS:
      return True
    else:
      return False

class WebSagatorError(Exception):
  """Web Sagator Error"""
  pass

def _recipient_condition(user,like='LIKE',op='OR'):
    user=DB.quote(user)
    return "( recipients %s '%s' %s recipients %s '%%,%s' %s recipients %s '%s,%%' %s recipients %s '%%,%s,%%' )" \
           % (like,user,op,like,user,op,like,user,op,like,user)

def _is_admin(user=None):
    '''
    Check, if user is an admin defined in config.
    '''
    if not user:
      user=LOGIN
    if user in WEBQ.ADMINS:
      return True
    return False

def _check_perm(qname,log=False):
    '''
    Find quarantined file in database and check, if it is delivered
    to currently logged user.
    '''
    if _is_admin():
      return True
    reg_user=re.compile('^(.+,|)%s(,.+|)$' % USER,re.I)
    for (r,) in DB.query("SELECT recipients FROM sagator WHERE qname='%s'" % DB.quote(qname)):
      if reg_user.search(r):
        return True
      elif log:
        debug.echo(1,"Permission denied for: user=%s, owner=%s" % (USER,r))
    return False

def _get_mail(qname,asis=0):
    '''
    Return a mail_class object with content of a quarantined email.
    It also checks permissions of logged user for this file.
    If asis is True, return whole quarantined file, otherwise
    return only it's DATA part.
    '''
    # check, if path contains dangerous subpaths
    if re.search('\.\.',qname):
      raise WebSagatorError,'Illegal path!'
    # check, if qname is in database
    if not _check_perm(qname):
      raise WebSagatorError,'Permission denied!'
    email=mail_class()
    # open mail data
    email_asis=open(safe.fn(qname)).read()
    if asis:
      email.comm=''
      email.data=email_asis
    else:
      reg_mail=re.compile('\W(.*?\nDATA\r?\n)(.*\n)\.\r?\n',re.I|re.M|re.S
                         ).search(email_asis)
      if not reg_mail:
        raise WebSagatorError,'Quarantine file content error!'
      email.comm=reg_mail.group(1)
      email.data=reg_mail.group(2)
    email.findbody()
    try:
      reg_smtp=re.compile('^MAIL +FROM: *(.*?)\r?\n',re.I|re.M|re.S
                         ).search(email_asis)
      email.sender=rfc822.parseaddr(reg_smtp.group(1))[1]
    except:
      pass
    return email

def _form_buttons(whichnot=[],qnames=[]):
    ret=''
    b=[('View',_('View')),
       ('Download',_('Download')),
       ('Recheck',_('Recheck')),
       ('Deliver',_('Deliver'))]
    if qnames:
      ret+='<form action="action"><p>\n'
    for k,v in b:
      if (not k in whichnot) and (k.lower() in WEBQ.PERMS):
        ret+='<input type="submit" name="submit" value="%s" class="submit"/>\n' % v
    if not whichnot:
      ret+='<input type="reset" value="%s" class="submit"/>\n' % _('Deselect all')
    for qname in qnames:
      ret+='<input type="hidden" name="qname" value="%s"/>\n' % qname
    if qnames:
      ret+='</p></form>\n'
    return ret

def _header(req,login=None):
    if _is_admin():
      login=_("admin")
    elif not login:
      login=LOGIN
    return HEADER % (
      _("ISO-8859-1"),
      _("web access for "),
      login,
      _('WebScan'),
      _('Settings'),
      _('Search')
    )

def index(req,offset=0,limit=-1,vname='',sender='',begin='',end='',recip=''):
    '''
    Main page. Parameters:
      offset - skip "offset" lines first
      limit - show max. "limit" lines
      vname - search for "vname" in virus name
      sender - search for "sender" in sender's email
      begin - show only records sent later than "begin" date
      end - show only record sent before "end" date
    '''
    if limit<0:
      limit=SHOWROWS
    # check arguments
    if (0<=int(offset)) and (0<=int(limit)<=200):
      NEXTREQ=""
      if _is_admin():
        if recip:
          NEXTREQ+="&amp;recip="+recip
          if recip[0]=="!":
            WHERE=_recipient_condition(
                    re.compile(WEBQ.USERCONV[0]).sub(WEBQ.USERCONV[1],recip[1:]),
                    'NOT LIKE','AND')
          else:
            WHERE=_recipient_condition(
                    re.compile(WEBQ.USERCONV[0]).sub(WEBQ.USERCONV[1],recip))
        else:
          WHERE="1=1"
      else:
        WHERE=_recipient_condition(USER)
      if vname:
        NEXTREQ+="&amp;vname="+vname
        if vname[0]=='!':
          WHERE+=" AND virname NOT LIKE '%s'" % DB.quote(vname[1:])
        else:
          WHERE+=" AND virname LIKE '%s'" % DB.quote(vname)
      if sender:
        NEXTREQ+="&amp;sender="+sender
        if sender[0]=='!':
          WHERE+=" AND sender NOT LIKE '%s'" % DB.quote(sender[1:])
        else:
          WHERE+=" AND sender LIKE '%s'" % DB.quote(sender)
      if begin:
        NEXTREQ+="&amp;begin="+begin
        WHERE+=" AND datetime>='%s'" % DB.quote(begin)
      if end:
        NEXTREQ+="&amp;end="+end
        WHERE+=" AND datetime<='%s'" % DB.quote(end)
      QUERY="SELECT datetime,level,virname,status,qname,sender,recipients,size FROM sagator WHERE %s ORDER BY datetime DESC" % WHERE
      if DB.name=='MySQLdb':
        QUERY+=" LIMIT %d,%d" % (int(offset),int(limit))
      else:
        QUERY+=" LIMIT %d OFFSET %d" % (int(limit),int(offset))
      debug.echo(4,'webaccess():',QUERY)
    else:
      raise ValueError,'Value out of range'
    # generate page
    pg=html.page(_header(req))
    pg.write('<form action="action">\n')
    pg.write('<table class="container">');
    # create main table content
    tab=html.table()
    if _is_admin():
      tab.addhdr('',_('Date and time'),_('Virus name (level)'),_('Sender'),
                 _('Size(B)'),_('Action'),_('Recipients'))
    else:
      tab.addhdr('',_('Date and time'),_('Virus name (level)'),_('Sender'),
                 _('Size(B)'),_('Action'))
    tab.cols={2:'style="text-align: center"',5:'style="text-align: right"',7:'style="white-space: normal"'}
    n=int(offset)
    for (datetime,level,virname,status,qname,sender,recipients,size) in DB.query(QUERY):
      n+=1
      if qname:
        CB='<input type="checkbox" name="qname" value="%s"/>' % qname
      else:
        CB='&nbsp;'
      if _is_admin():
        tab.add(CB,datetime,'%s (%1.2f)' % (virname,level),sender,size,_(status),recipients.replace(',',', '))
      else:
        tab.add(CB,datetime,'%s (%1.2f)' % (virname,level),sender,size,_(status))
    # write forms before and later and write main table between
    for three in range(3):
      if three==1:
        pg.write('<tr><td colspan="3">'+str(tab)+'</td></tr>\n')
        continue
      pg.write('<tr>')
      if int(offset)>0:
        pg.write('<td style="width: 25%%; text-align: left"><a href="?offset=%d&amp;limit=%d%s" class="submit">&lt;&lt;&lt; %s</a></td>' \
                 % (max(0,n-2*int(limit)),int(limit),NEXTREQ,_('Previous page')))
      else:
        pg.write('<td style="width: 25%%">&nbsp;</td>')
      pg.write('<td style="text-align: center">%s</td>' % _form_buttons(['Deliver']))
      if n>=int(offset)+int(limit):
        pg.write('<td style="width: 25%%; text-align: left"><a href="?offset=%d&amp;limit=%d%s" class="submit">%s &gt;&gt;&gt;</a></td>' \
                 % (n,int(limit),NEXTREQ,_('Next page')))
      else:
        pg.write('<td style="width: 25%%">&nbsp;</td>')
      pg.write('</tr>\n')
    pg.write('</table>\n</form>\n')
    return pg.getvalue()

def search(req):
    ''' Show search form '''
    if _is_admin():
      pg=html.page(_header(req)+_(SEARCH_FORM) \
           % _('<tr><td>Recipient:</td><td><input type="text" name="recip"/></td></tr>'))
    else:
      pg=html.page(_header(req)+_(SEARCH_FORM) % '')
    return pg.getvalue()

def view(req,qname,asis=0):
    '''
    View emails contents
    
    If asis is not zero, content is displayed as is (as a quarantine
    file content).
    '''
    pg=html.page(_header(req))
    # required header fields
    req=['from:','to:','subject:','date:']
    if type(qname)!=type([]):
      qnames=[qname]
    else:
      qnames=qname
    pg.write(_form_buttons(['View'],qnames))
    for qname in qnames:
      pg.write('<hr/>')
      try:
        email=_get_mail(qname,asis)
      except IOError:
        pg.write(html.errtab(_("Can't open quarantine file for %s") % qname))
        continue
      # get some headers
      hdr=''
      f=StringIO(email.data[:email.bodypos])
      line=0
      required_hdr=False
      while True:
        line+=1
        l=f.readline()
        if not l: break
        if email.reg_header(l):
          required_hdr=False
          if 0 in [l.lower().find(x) for x in req]:
            hdr+=l
            required_hdr=True
          continue
        else:
          if required_hdr:
            hdr+=l
      pg.write('<pre><b>%s</b>\n\n%s</pre>' \
            % (html.specialchars(hdr.rstrip()),
               html.specialchars(email.data[email.bodypos:])))
    return pg.getvalue()

def download(req,qname,asis=0):
    '''
    Return email as a downloadable mailbox content
    '''
    if type(qname)!=type([]):
      qnames=[qname]
    else:
      qnames=qname
    if len(qnames)==1:
      # send as RFC822 message, if only one mail is requested
      try:
        email=_get_mail(qnames[0],asis)
      except IOError:
        return html.error(_("Can't open quarantine file for %s") % qnames[0])
      req.content_type='message/rfc822'
      return email.data
    else:
      # send as mailbox if more than one mail are requested
      io=StringIO()
      errs=html.page(_header(req))
      errcount=0
      for qname in qnames:
        try:
          email=_get_mail(qname,asis)
          io.write(fromhdr(email.sender))
          io.write(email.data)
          io.write("\n")
        except IOError:
          errs.write(html.errtab("Can't open quarantine file for %s" % qname))
          errcount+=1
      if errcount>0:
        errs.write(_('<p>Download aborted!</p>'))
        return errs.getvalue()
      req.content_type='text/plain'
      return io.getvalue()

def recheck(req,qname):
    '''
    Check again for viruses/spams
    '''
    if type(qname)!=type([]):
      qnames=[qname]
    else:
      qnames=qname
    pg=html.page(_header(req))
    pg.write(_form_buttons(['Recheck'],qnames))
    WEB_SCANNER=WEBQ.SCANNER
    WEB_SCANNER.reinit()
    tab=html.table()
    tab.addhdr(_('QName'),_('Virus name (level)'),_('Scanner'),_('Scanner output'))
    for qname in qnames:
      try:
        mail.data=_get_mail(qname).data
      except IOError:
        tab.tr='<tr style="background: yellow; color: black">'
        tab.add(qname,_('ERROR'),'',"%s" % _("Can't open file!"))
        continue
      level,detected,virlist,scan_reply,err=do_scan(WEB_SCANNER)
      if err:
        tab.tr='<tr style="background: yellow">'
        tab.add(qname,_('ERROR'),'',"<pre>%s</pre>" % err)
        continue
      if is_infected(level,detected):
        tab.tr='<tr style="background: red; color: white">'
        tab.add(qname,'%s (%1.2f)' % (detected,level),globals.found_by.name,'<pre>%s</pre>' % '\n'.join(virlist))
      else:
        tab.tr='<tr style="background: green; color: white">'
        tab.add(qname,_('CLEAN'),'&nbsp;','<pre> </pre>')
    pg.write(str(tab))
    return pg.getvalue()

def deliver(req,qname,user=''):
    '''
    Deliver this message to logged recipient.
    '''
    if type(qname)!=type([]):
      qnames=[qname]
    else:
      qnames=qname
    pg=html.page(_header(req))
    user=USER
    try:
      pg.write('<pre>'+send_qfile(safe.fn(qnames[0]),SMTP_SERVER,user))
      pg.write('</pre>\n%s\n' % _('Mail sent.'))
    except:
      pg.write(_('Error sending message %s') % qnames[0])
    return pg.getvalue()

def action(req,submit,qname=None,asis=0):
    ''' Main form actions '''
    # lynx fix (sends more submits)
    if type(submit)==type([]):
      submit=submit[0]
    if qname==None:
      return _header(req)+_(WRONG_PARAMS)
    if (submit==_('View')) and ('view' in WEBQ.PERMS):
      return view(req,qname,asis)
    elif (submit==_('Download')) and ('download' in WEBQ.PERMS):
      return download(req,qname,asis)
    elif (submit==_('Recheck')) and ('recheck' in WEBQ.PERMS):
      return recheck(req,qname)
    elif (submit==_('Deliver')) and ('deliver' in WEBQ.PERMS):
      return deliver(req,qname)
    return html.error(_('Unknown action!')+'(%s,%s)' % (submit,_.LANG))

# WEB SCANNER
def webscan(req,**args):
    '''
    WEB scanner (scan uploaded files for viruses)
    '''
    pg=html.page(_header(req))
    if req.method == 'GET':
      try:
        num=int(args['n'])
      except:
        num=1
      pg.write('<form action="webscan" method="post" enctype="multipart/form-data">\n')
      for i in range(num,0,-1):
        pg.write('<input type="file" name="file%04d" size="80"><br/>\n' % i)
      pg.write('<input type="submit" class="submit">\n</form>\n')
      return pg.getvalue()
    WEB_SCANNER=WEBQ.SCANNER
    WEB_SCANNER.reinit()
    for reqfile in args.values():
      try:
        globals.reset()
        mail.filename=reqfile.filename
        mail.data=reqfile.file.read()
        mail.comm="NOOP WEBSAGATOR, remote host: %s" \
          % (req.get_remote_host(2))
        level,detected,virlist,scan_reply,err=do_scan(WEB_SCANNER)
        if is_infected(level,detected):
          d="[%s,%0.2f]" % (globals.found_by.name,level)
          pg.write('%s: <div style="color: red">%s</div> %s<br/>\n' \
            % (reqfile.filename,detected,d))
        else:
          pg.write('%s: %s<br/>\n' % (reqfile.filename,_('no virus detected')))
      except:
        pg.write('%s: %s<br/>\n' % (str(reqfile),_("can't be scanned")))
    return pg.getvalue()


def settings(req,old='',new='',retyped='',lang='',rows=0):
    ''' Change password for logged user. '''
    global SHOWROWS
    row_min,row_max=5,200
    LANGUAGES=('en','sk')
    msg=''
    td='colspan="2" style="text-align: center"'
    # set language
    if lang:
      if lang in LANGUAGES:
        _.set_lang(lang,LOCALE_DIR)
        DB.execute("UPDATE webaccess SET lang='%s' WHERE email='%s'" \
                   % (DB.quote(lang),DB.quote(LOGIN)))
        msg+=html.msgrow(_('Language settings stored.'),td=td)
      else:
        msg+=html.errrow(_('Unknown language!'),td=td)
    if rows:
      try:
        SHOWROWS=int(rows)
        if row_min<=SHOWROWS<=row_max:
          DB.execute("UPDATE webaccess SET showrows=%d WHERE email='%s'" \
                     % (SHOWROWS,DB.quote(LOGIN)))
          msg+=html.msgrow(_('Row count settings stored.'),td=td)
        else:
          raise ValueError
      except ValueError:
        msg+=html.errrow(_('Wrong value for row count!'),td=td)
    if (not old) or (not new) or (not retyped):
      langtab=[]
      current_lang=_.LANG
      for lang in LANGUAGES:
        _.set_lang(lang,LOCALE_DIR)
        langtab.append((lang,_('English')))
      _.set_lang(current_lang,LOCALE_DIR)
      pg=html.page(_header(req)+_(SETTINGS) \
                   % (msg,html.select('lang',langtab,_.LANG),SHOWROWS))
      return pg.getvalue()
    # check password
    if _check_pass(LOGIN,old):
      if new==retyped:
        # change this password
        cryptpass=crypt.crypt(new,'$1$'+randomchars(8))
        q=DB.execute("UPDATE webaccess SET pass='%s' WHERE email='%s'" \
                     % (DB.quote(cryptpass),DB.quote(LOGIN)))
        return _('Password changed successfully. You must <a href="index">relog</a>.')
      else:
        return html.page(_header(req)+_(SETTINGS) %
                         html.errrow(_('New and retyped passwords are different!'),
                                     td=td)
                         ).getvalue()
    else:
      return html.page(_header(req)+_(SETTINGS) %
                       html.errrow(_('Wrong old password!'),td=td)
                       ).getvalue()

def stats(req):
    ''' Show mail account statistics '''
    pg=html.page(_header(req))
    spam_count=DB.query("SELECT count(*) FROM sagator WHERE virname LIKE '%%SPAM%%' AND recipients LIKE '%%%s%%'" % DB.quote(USER))[0][0]
    virus_count=DB.query("SELECT count(*) FROM sagator WHERE virname NOT LIKE '%%SPAM%%' AND recipients LIKE '%%%s%%'" % DB.quote(USER))[0][0]
    system_spam_count=DB.query("SELECT count(*) FROM sagator WHERE virname LIKE '%%SPAM%%'")[0][0]
    system_virus_count=DB.query("SELECT count(*) FROM sagator WHERE virname NOT LIKE '%%SPAM%%'")[0][0]
    total_perc=(spam_count+virus_count)/100.0
    system_perc=(system_spam_count+system_virus_count)/100.0
    pg.write(_("Your spams: %d (%d%%)<br/>") % (spam_count,spam_count/total_perc))
    pg.write(_("Your viruses: %d (%d%%)<br/>") % (virus_count,virus_count/total_perc))
    pg.write("<br/>")
    pg.write(_("Spams on this system: %d (%d%%)<br/>") \
             % (system_spam_count,system_spam_count/system_perc))
    pg.write(_("Viruses on this system: %d (%d%%)<br/>") \
             % (system_virus_count,system_virus_count/system_perc))
    return pg.getvalue()
