## ------------------------------------------------------------------------
## iSiLK - A python-based graphical user interface for SiLK
## ------------------------------------------------------------------------
## Copyright (c) 2007-2008 Carnegie Mellon University. All Rights Reserved.
## ------------------------------------------------------------------------
## Authors: Andrew Kompanek (ajk@cert.org)
## ------------------------------------------------------------------------
## GNU General Public License (GPL) Rights pursuant to Version 2, June 1991
## Government Purpose License Rights (GPLR) pursuant to DFARS 252.225-7013
## ------------------------------------------------------------------------
## See the LICENSE file included in this distribution
## ------------------------------------------------------------------------

import wx, wx.lib.stattext, wx.stc
import string
#import pdb

# ---- Custom Events -----
# Option change event generated by tool option editors
EVTTYPE_OPTION_CHANGE = wx.NewEventType()
EVT_OPTION_CHANGE = wx.PyEventBinder(EVTTYPE_OPTION_CHANGE, 1)
# Event for communicating status back to a view of the data set
EVTTYPE_UPDATE_STATUS = wx.NewEventType()
EVT_UPDATE_STATUS = wx.PyEventBinder(EVTTYPE_UPDATE_STATUS, 1)
# Event for communicating that a command has been launched
EVTTYPE_LOG_COMMAND = wx.NewEventType()
EVT_LOG_COMMAND= wx.PyEventBinder(EVTTYPE_LOG_COMMAND, 1)
# Events for resultbook class
EVTTYPE_RESULTBOOK_VIEW_CHANGED = wx.NewEventType()
EVT_RESULTBOOK_VIEW_CHANGED = wx.PyEventBinder(EVTTYPE_RESULTBOOK_VIEW_CHANGED)
# Events for library class
EVTTYPE_LIBRARY_ANALYSIS_ADDED = wx.NewEventType()
EVT_LIBRARY_ADD_ANALYSIS = wx.PyEventBinder(EVTTYPE_LIBRARY_ANALYSIS_ADDED)
EVTTYPE_LIBRARY_ANALYSIS_DELETED = wx.NewEventType()
EVT_LIBRARY_ANALYSIS_DELETED = wx.PyEventBinder(EVTTYPE_LIBRARY_ANALYSIS_DELETED)
EVTTYPE_RESULT_CHANGED = wx.NewEventType()
EVT_RESULT_CHANGED = wx.PyEventBinder(EVTTYPE_RESULT_CHANGED, 1)

class OptionChangeEvent (wx.PyCommandEvent):
    def __init__(self, id, optName, optVal):
        wx.PyCommandEvent.__init__(self, EVTTYPE_OPTION_CHANGE, id)
        self.optName = optName
        self.optVal = optVal

       
# Pattern of a result.  Used to guess size of a control
field_value_patterns = {
    "recnum" :"XXXXXXXX",
    "#"      :"XXXXXXXX",
    "class"  :"XXXXX",
    "type"   :"XXXXX",
    "sip"    :"XXX.XXX.XXX.XXX     ",
    "dip"    :"XXX.XXX.XXX.XXX     ",
    "nhip"   :"XXXXXXX ",
    "sport"  :"XXXXX  ",
    "dport"  :"XXXXX  ",
    "icmp_type"    : "XXXXXXX",
    "icmp_code"    : "XXXXXXX",
    "proto"  :"XXX  ",
    "packets":"XXXX,XXX,XXX  ",
    "bytes"  :"XXX,XXX,XXX,XXX",
    "records"  :"XXX,XXX,XXX",    
    "unique_sip"  :"XXX,XXX,XXX,XXX",
    "unique_dip"  :"XXX,XXX,XXX",        
    "flags"  :"XXXXXXXX",
    "stime"  :"XXXX/XX/XXTXX:XX:XX          ",
    "dur"    :"XXXXXX",
    "etime"  :"XXXX/XX/XXTXX:XX:XX          ",
    "date"   :"XXXX/XX/XXTXX:XX:XX     ",    
    "sensor" :"XXXXXX",
    "sval"   :"XXXXXXXXX",
    "dval"   :"XXXXXXXXX",    
    }
    
# Justification to use when displaying value in a list control
justify = {
    "#"      :wx.LIST_FORMAT_LEFT,
    "sip"    :wx.LIST_FORMAT_LEFT,
    "dip"    :wx.LIST_FORMAT_LEFT,
    "nhip"   :wx.LIST_FORMAT_LEFT,
    "sport"  :wx.LIST_FORMAT_RIGHT,
    "dport"  :wx.LIST_FORMAT_RIGHT,
    "proto"  :wx.LIST_FORMAT_RIGHT,
    "packets":wx.LIST_FORMAT_RIGHT,
    "bytes"  :wx.LIST_FORMAT_RIGHT,
    "records":wx.LIST_FORMAT_RIGHT,
    "unique_sip"  :wx.LIST_FORMAT_RIGHT,
    "unique_dip":wx.LIST_FORMAT_RIGHT,    
    "flags"  :wx.LIST_FORMAT_LEFT,
    "stime"  :wx.LIST_FORMAT_LEFT,
    "dur"    :wx.LIST_FORMAT_RIGHT,
    "etime"  :wx.LIST_FORMAT_LEFT,
    "date"   :wx.LIST_FORMAT_LEFT,    
    "sensor" :wx.LIST_FORMAT_RIGHT,
    "sval"   :wx.LIST_FORMAT_LEFT,    
    "dval"   :wx.LIST_FORMAT_LEFT,        
    }
    
def init():
    pass 
    
XSMALL_FONT_SIZE = None    
SMALL_FONT_SIZE = None
LARGE_FONT_SIZE = None
XLARGE_FONT_SIZE = None

def XSmFont(*args, **kargs):
    global XSMALL_FONT_SIZE
    if wx.Platform == '__WXMSW__':
        XSMALL_FONT_SIZE = 7
    if not XSMALL_FONT_SIZE:
        t = wx.StaticText(None, -1, "")
        # FIXME: Mac behavior doesn't make any sense to me
        if wx.Platform == "__WXMAC__":
            XSMALL_FONT_SIZE = t.GetFont().GetPointSize() 
        else:
            XSMALL_FONT_SIZE = t.GetFont().GetPointSize() - 2
    return wx.Font(XSMALL_FONT_SIZE, *args, **kargs)    

def SmFont(*args, **kargs):
    global SMALL_FONT_SIZE
    if wx.Platform == '__WXMSW__':
        SMALL_FONT_SIZE = 8
    if not SMALL_FONT_SIZE:
        t = wx.StaticText(None, -1, "")
        # FIXME: Mac behavior doesn't make any sense to me
        if wx.Platform == "__WXMAC__":
            SMALL_FONT_SIZE = t.GetFont().GetPointSize() + 1
        else:
            SMALL_FONT_SIZE = t.GetFont().GetPointSize() - 1            
    return wx.Font(SMALL_FONT_SIZE, *args, **kargs)

def LgFont(*args, **kargs):
    global LARGE_FONT_SIZE
    if wx.Platform == '__WXMSW__':
        LARGE_FONT_SIZE = 12
    if not LARGE_FONT_SIZE:
        t = wx.StaticText(None, -1, "")
        if wx.Platform == "__WXMAC__":
            LARGE_FONT_SIZE = t.GetFont().GetPointSize() + 4
        else:
            LARGE_FONT_SIZE = t.GetFont().GetPointSize() +2
    return wx.Font(LARGE_FONT_SIZE, *args, **kargs)    
    
    
def XLgFont(*args, **kargs):
    global XLARGE_FONT_SIZE
    if wx.Platform == '__WXMSW__':
        XLARGE_FONT_SIZE = 12
    if not XLARGE_FONT_SIZE:
        t = wx.StaticText(None, -1, "")
        if wx.Platform == "__WXMAC__":
            XLARGE_FONT_SIZE = t.GetFont().GetPointSize() + 6
        else:
            XLARGE_FONT_SIZE = t.GetFont().GetPointSize() +4
    return wx.Font(XLARGE_FONT_SIZE, *args, **kargs)

def Font(*args, **kargs):
    if wx.Platform == '__WXMSW__':
        font_size = 10
    else:
        t = wx.TextCtrl(None, -1, "")    
        font_size = t.GetFont().GetPointSize()
    return wx.Font(font_size, *args, **kargs)    
    
class SmStaticLabel(wx.StaticText):
    def __init__(self, *args, **kargs):
        wx.StaticText.__init__(self, *args, **kargs)
        self.SetFont(SmFont(wx.SWISS, wx.NORMAL, wx.NORMAL))
        
class __SmStaticText(wx.StaticText):
    def __init__(self, *args, **kargs):
        wx.StaticText.__init__(self, *args, **kargs)
        self.SetFont(SmFont(wx.SWISS, wx.NORMAL, wx.NORMAL))
        
class SmHyperlinkCtrl(wx.HyperlinkCtrl):
    def __init__(self, *args, **kargs):
        wx.HyperlinkCtrl.__init__(self, *args, **kargs)
        self.SetFont(SmFont(wx.SWISS, wx.NORMAL, wx.NORMAL, True))
        self.SetHoverColour(wx.Color(0, 0, 255))        
        self.SetNormalColour(wx.Color(0,0,0))
        self.SetVisitedColour(wx.Color(0,0,0))

class XLgStaticText(wx.StaticText):
    def __init__(self, *args, **kargs):
        wx.StaticText.__init__(self, *args, **kargs)
        self.SetFont(XLgFont(wx.SWISS, wx.NORMAL, wx.NORMAL))
        
class LgStaticText(wx.StaticText):
    def __init__(self, *args, **kargs):
        wx.StaticText.__init__(self, *args, **kargs)
        self.SetFont(LgFont(wx.SWISS, wx.NORMAL, wx.NORMAL))


class LgBoldStaticText(wx.StaticText):
    def __init__(self, *args, **kargs):
        wx.StaticText.__init__(self, *args, **kargs)
        self.SetFont(LgFont(wx.SWISS, wx.BOLD, wx.NORMAL))

class SmStaticText(wx.StaticText):
    def __init__(self, *args, **kargs):
        wx.StaticText.__init__(self, *args, **kargs)
        self.SetFont(SmFont(wx.SWISS, wx.NORMAL, wx.NORMAL))
        
class XSmStaticText(wx.StaticText):
    def __init__(self, *args, **kargs):
        wx.StaticText.__init__(self, *args, **kargs)
        self.SetFont(XSmFont(wx.SWISS, wx.NORMAL, wx.NORMAL))

class SmBoldStaticLabel(wx.StaticText):
    def __init__(self, *args, **kargs):
        wx.StaticText.__init__(self, *args, **kargs)
        self.SetFont(SmFont(wx.SWISS, wx.NORMAL, wx.NORMAL))
        
class SmCheckBox(wx.CheckBox):
    def __init__(self, *args, **kargs):
        wx.CheckBox.__init__(self, *args, **kargs)
        self.SetFont(SmFont(wx.SWISS, wx.NORMAL, wx.NORMAL))

class SmChoiceCtrl(wx.Choice):
    def __init__ (self, *args, **kargs):
        wx.Choice.__init__(self, *args, **kargs)
        self.SetFont(SmFont(wx.SWISS, wx.NORMAL, wx.NORMAL))

class SmPortChoiceCtrl(SmChoiceCtrl):
    def __init__ (self, *args):
        opts = ["Any", "TCP", "SSH", "SMTP", "Other"]
        SmChoiceCtrl.__init__(self, *args)
        for o in opts: self.Append(o)
        self.SetSelection(0)        

class SmProtoChoiceCtrl(SmChoiceCtrl):
    def __init__ (self, *args):
        opts = ["Any", "TCP", "UDP", "ICMP", "Other"]
        SmChoiceCtrl.__init__(self, *args)
        for o in opts: self.Append(o)
        self.SetSelection(0)

class SmIPTextCtrl(wx.TextCtrl):
    def __init__(self, *args):
        wx.TextCtrl.__init__(self, *args)
        self.SetValue("xxx.xxx.xxx.xxx")
        self.SetFont(SmFont(wx.SWISS, wx.NORMAL, wx.NORMAL))
        
class SmStyledTextCtrl(wx.stc.StyledTextCtrl):
    def __init__(self, *args, **kargs):
        wx.stc.StyledTextCtrl.__init__(self, *args, **kargs)
        #self.SetFont(SmFont(wx.SWISS, wx.NORMAL, wx.NORMAL))       

class XSmTextCtrl(wx.TextCtrl):
    def __init__(self, *args, **kargs):
        wx.TextCtrl.__init__(self, *args, **kargs)
        self.SetFont(XSmFont(wx.SWISS, wx.NORMAL, wx.NORMAL))

class SmTextCtrl(wx.TextCtrl):
    def __init__(self, *args, **kargs):
        wx.TextCtrl.__init__(self, *args, **kargs)
        self.SetFont(SmFont(wx.SWISS, wx.NORMAL, wx.NORMAL))

class SmButton(wx.Button):
    def __init__(self, *args, **kargs):
        wx.Button.__init__(self, *args, **kargs)
        self.SetFont(SmFont(wx.SWISS, wx.NORMAL, wx.NORMAL))
        
class XSmButton(wx.Button):
    def __init__(self, *args, **kargs):
        wx.Button.__init__(self, *args, **kargs)
        self.SetFont(XSmFont(wx.SWISS, wx.NORMAL, wx.NORMAL))

def GetTextAlignment(field):
    if field in justify:
        return justify[field]
    else:
        return wx.LIST_FORMAT_LEFT

def GetTextPattern(field):
    # Dummy used to assign the default width to each column.
    if field in field_value_patterns:
        return field_value_patterns[field]
    else:
        return "XXXXXXXXXXXXX"

def GetDescriptionText(field):
    if field == "proto": return "Protocol (0-255)"
    if field == "sip"  : return "Source IP Address (xxx.xxx.xxx.xx)"
    if field == "dip"  : return "Destination IP Address (xxx.xxx.xxx.xxx)"
    if field == "nhip" : return "Next Hop IP Address"   
    if field == "sport": return "Source Port (0-65535)"
    if field == "dport": return "Destination Port (0-65535)"
    if field == "stime": return "Start Time"
    if field == "dur"  : return "Duration"
    if field == "etime": return "End Time"
    if field == "flags": return "TCP Flags"
    if field == "bytes": return "Byte Count"
    if field == "packets": return "Packets Count"    
    if field == "flows": return "Flow Count"
    if field == "records": return "Flow record count"    
    if field == "sensor": return "Sensor"
    if field == "date": return "Date"
    return "*ERR*"

def GetLabelText(field):
    return field

class BasicValidator(wx.PyValidator):
    def __init__(self, valid_chars, validate_func=None):
        wx.PyValidator.__init__(self)
        self.Bind(wx.EVT_CHAR, self.OnChar)
        self.valid_chars = valid_chars

    def Validate(self, win):
        if self.valid_chars == None:
            return True
        tc = self.GetWindow()
        val = tc.GetValue()
        for x in val:
            if x not in self.valid_chars:
                return False
        return True

    def OnChar(self, event):
        if self.valid_chars == None:
            event.Skip()
            return
        key = event.GetKeyCode()
        if key < wx.WXK_SPACE or key == wx.WXK_DELETE or key > 255:
            event.Skip()
            return
        if event.CmdDown():
            event.Skip()
            return    
        if chr(key) in self.valid_chars:
            event.Skip()
            return
        if not wx.Validator_IsSilent():
            wx.Bell()
        # Returning without calling even.Skip eats the event before it
        # gets to the text control
        return

    def TransferToWindow(self):
        return True # Prevent wxDialog from complaining.
    def TransferFromWindow(self):
        return True # Prevent wxDialog from complaining.


class NameValidator(BasicValidator):
    def __init__(self):
        valid_chars = string.letters + string.digits + "_" + "-" + " " + "."
        BasicValidator.__init__(self, valid_chars)
    def Clone(self):
        return NameValidator()
        
# FIXME: what are the constraints, really?
# sval or dval label
class LabelValidator(BasicValidator):
    def __init__(self):
        valid_chars = string.letters + string.digits + "_" + "-" + "." + ":" + ","
        BasicValidator.__init__(self, valid_chars)
    def Clone(self):
        return LabelValidator()        

# Since pmap keys can contain spaces, we implicitly quote them.  But therefore
# the keys themselves cannot contain quote characters
class PmapKeyValidator(BasicValidator):
    def __init__(self):
        valid_chars = string.letters + string.digits + "_" + "-" + " " + "."
        BasicValidator.__init__(self, valid_chars)
    def Clone(self):
        return PmapKeyValidator()
