Package Gnumed :: Package wxpython :: Module gmCryptoText |
[frames] | no frames]

# Source Code for Module Gnumed.wxpython.gmCryptoText

      1  #!/usr/bin/python
      2  #############################################################################
      3  #
      4  # gmCryptoText - implements a "crypto" aware text widget
      5  # ---------------------------------------------------------------------------
      6  # This text widget allows arbitrary text to be entered via keyboard,
      7  # cut'n paste via clipboard, or text files via drag'n drop
      8  # Right clicking pops up a menu that allows to encrypt or decrypt
      9  # the selected text segment.
     10  #
     11  # @author: Dr. Horst Herb
     12  # @copyright: author
     13  # @license: GPL v2 or later (details at http://www.gnu.org)
     14  # @dependencies: wxPython (>= version 2.3.1)
     15  # @change log:
     16  #       30.06.2001 hherb initial implementation, untested
     17  #       25.10.2001 commenting of source, module test enabled, debug log inserts removed
     18  #
     19  # @TODO:
     20  #       - all runtime error checking / exception handling
     21  #       - plug in structure for ciphers, rich popup menu selection of crypto methods
     22  #       - use Python OpenSSL wrappers or GnuPG wrapper!
     23  #       - tagging of the used cipher and the user within the encrypted text
     24  #       - timer that expires pas phrase after arbitrary time intervals
     25  #       - implement a "rich text" widget
     26  ############################################################################
     27
     28
     29  """This module implements a ""crypto"" aware text widget
     30
     31  This text widget allows arbitrary text to be entered via keyboard,
     32  cut'n paste via clipboard, or text files via drag'n drop
     33  Right clicking pops up a menu that allows to encrypt or decrypt
     34  the selected text segment.
     35  """
     36
     37  try:
     38          import wxversion
     39          import wx
     40  except ImportError:
     41          from wxPython import wx
     42
     43  import string, rotor, binascii
     44
     45  ID_POP_ENCRYPT = wx.NewId()
     46  ID_POP_DECRYPT = wx.NewId()
     47  ID_POP_PASSPHRASE = wx.NewId()
     48
     49 -class gmTextctrlFileDropTarget(wx.FileDropTarget):
     50      """ a generic text control widget that accepts dropped files """
     51
     52 -    def __init__(self, textwindow):
     53          wx.FileDropTarget.__init__(self)
     54          self.textwindow=textwindow
     55
     56 -    def OnDropFiles(self, x, y, filenames):
     57          """inserts the dropped file(s) content(s) at the cursor position"""
     58          for file in filenames:
     59              self.textwindow.WriteText(string.join(open(file, 'r').readlines()))
     60
     61
     62 -class gmCryptoText(wx.TextCtrl):
     63      """A special text widget that supports cryptography
     64
     65      A right mouse click pops up a manu that allows to encrypt
     66      or decrypt selected text segments.
     67      You can drag and drop any number of text files into the text
     68      widget, and that text will be inserted at the current cursor
     69      position
     70      """
     71
     72 -    def __init__(self, parent, id, size=wx.DefaultSize, style=wx.TE_MULTILINE|wx.TE_RICH, defaulttext=None):
     73          #initialize parent class
     74          wx.TextCtrl.__init__(self, parent, id, size=size, style=style)
     75          self.SetDefaultStyle(wx.TextAttr(wx.RED))
     76
     77          #will search for text tags within fuzzymargin characters
     78          self.fuzzymargin = 25
     79          self.passphrase = None
     80          #time in seconds until passphrase expires
     81          self. passphrase_expiry = 120
     82          #the text selected for encryption/decryption
     83          self.textselection = None
     84          self. selectionStart = 0
     85          self.selectionEnd = 0
     86
     87          if defaulttext is not None:
     88                  self.WriteText(defaulttext)
     89
     90          #a reserved ID for events related to this widget
     91          self.aID = wx.NewId()
     92
     93          #make this text widget a drop target for drag&dropped text files
     94          dt = gmTextctrlFileDropTarget(self)
     95          self.SetDropTarget(dt)
     96
     97          #bugger... this one for wxGTK
     98          wx.EVT_RIGHT_UP(self,self.OnRightClick)
     99          wx.EVT_RIGHT_DOWN(self, self.OnRightDown)
    100
    101          #...and this one for wxMSW (hope this inconsistency is fixed soon
    102          #wx.EVT_COMMAND_RIGHT_CLICK(self, self.aID, self.OnRightClick)
    103
    104 -    def OnRightClick(self, event):
    105          "A right mouse click triggers a popup menu for cryptographic functionality"
    106
    107          self.selectionStart, self.selectionEnd = self.GetSelection()
    108
    109          #create a popup menu
    110          menu = wx.Menu()
    111          menu.Append(ID_POP_ENCRYPT, _("Encrypt"))
    112          menu.Append(ID_POP_DECRYPT, _("Decrypt"))
    113          menu.Append(ID_POP_PASSPHRASE, _("Set pass phrase"))
    114
    115          #connect the events to event handler functions
    116          wx.EVT_MENU(self, ID_POP_ENCRYPT, self.OnEncrypt)
    117          wx.EVT_MENU(self, ID_POP_DECRYPT, self.OnDecrypt)
    118          wx.EVT_MENU(self, ID_POP_PASSPHRASE, self.OnSetPassphrase)
    119
    120          #show the menu
    121          self.PopupMenu(menu, wxPoint(event.GetX(), event.GetY()))
    122
    123          #free resources
    124          menu.Destroy()
    125
    126          #anybody else needs to intercept right click events?
    127          event.Skip()
    128
    129
    130          def OnContextMenu(self, event):
    131                  pass
    132
    133
    134 -    def OnEncrypt(self, event):
    135          """triggered by popup contect menu event"""
    136
    137          #get the selected text if any
    138          self.textselection = self.GetValue()[self.selectionStart:self.selectionEnd]
    139          #anything to do?
    140          if len(self.textselection)<1:
    141              return
    142
    143          #we can't crypt without passphrase, so ask for it if needed!
    144          if self.passphrase is None:
    145              self.passphrase = self.AskForPassphrase()
    146              if self.passphrase == None:
    147                  return
    148          #In order to be displayed, binary crypt output has to be 'hexlified'
    149          #encrypted text is tagged with <! ... !>
    150          #future versions will embed a algorithm tag here
    151          self.Replace(self.selectionStart, self.selectionEnd, \
    152                       '<!' + self.GetIdentTag() + binascii.hexlify(self.Encrypt(self.textselection, self.passphrase)) + '!>')
    153
    154
    155 -    def OnDecrypt(self, event):
    156
    157          if self.passphrase is None:
    158              self.passphrase = self.AskForPassphrase()
    159              if self.passphrase == None:
    160                  return
    161
    162          textselection, self.selectionStart, self.selectionEnd = \
    163              self.FuzzyScanSelection(self.selectionStart, self.selectionEnd, self.fuzzymargin)
    164          #is the selection tagged as encrypted ?
    165          if textselection[:2] != '<!' or textselection[-2:] != '!>':
    166              wx.MessageBox(_("This is not correctly encrypted text!"))
    167              return
    168          #get rid of the tags
    169          textselection = textselection[2:-2]
    170          identtag, textselection = self.StripIdentTag(textselection)
    171          #self.textselection = self.textselection[len(identtag):]
    172          #and don't forget to unhexlify the ciphertext before you feed it to the crypt
    173          decoded = self.Decrypt(binascii.unhexlify(textselection), self.passphrase, identtag)
    174          self.Replace(self.selectionStart, self.selectionEnd, decoded)
    175
    176
    177 -    def OnSetPassphrase(self, event):
    178          self.passphrase = self.AskForPassphrase()
    179
    180
    181 -    def OnRightDown(self, event):
    182          """dummy function; if this event was not intercepted, GTK would
    183          clear the text selection the very moment the mouse button is clicked"""
    184          pass
    185
    186 -    def AskForPassphrase(self):
    187          """asks for a pass phrase and returns it"""
    188          dlg = wxTextEntryDialog(self, _("Please enter your pass phrase:"), _("Pass phrase expired"), style=wxOK|wxCANCEL|wx.CENTRE|wx.TE_PASSWORD)
    189          if dlg.ShowModal() == wx.ID_OK:
    190              retval = dlg.GetValue()
    191          else:
    192              retval = None
    193          dlg.Destroy()
    194          return retval
    195
    196
    197 -    def Encrypt(self, cleartext, key):
    198          """override this function for your own crypto funcs"""
    199          rt = rotor.newrotor(key, 12)
    200          return rt.encrypt(cleartext)
    201
    202
    203 -    def Decrypt(self, ciphertext, key, identtag):
    204          """override this function for your own crypto funcs"""
    205          rt = rotor.newrotor(key, 12)
    206          return rt.decrypt(ciphertext)
    207
    208
    209 -    def StripIdentTag(self, text):
    210          """Remove the 'ident tag' from text and return both tag and test"""
    211          if text[0] != '[':
    212              "No ident tag ?"
    213              return '', text
    214          try:
    215              endtag = string.index(text, ']')+1
    216          except ValueError:
    217              return '', text
    218          return text[:endtag], text[endtag:]
    219
    220
    221 -    def GetIdentTag(self):
    222          """This is a 'virtual' function which should be overridden to provide your own meaningful tag"""
    223          return '[rotor]'
    224
    225
    226 -    def SetFuzzyMargin(self, margin):
    227          """The fuzzy margin is the number of characters on each side of the text selection
    228          the decryption algorithm will search for correct delimiters. It should be at least as long as
    229          the IdentTag is plus an extra 3 characters to allow for the crypto tag"""
    230          self.fuzzymargin = margin
    231
    232
    233 -    def FuzzyScanSelection(self, frompos, topos, margin):
    234          fulltext = self.GetValue()
    235          #search left margin
    236          start = frompos - margin
    237          if start < 0: start = 0
    238          if frompos == 0: frompos = 1
    239          #search right margin
    240          finish = topos + margin
    241          if finish > len(fulltext): finish = len(fulltext)
    242          if topos > len(fulltext)-2: topos = len (fulltext)-2
    243          try:
    244              left = string.rindex(fulltext, '<', start, frompos)
    245              right = string.index(fulltext, '>', topos, finish)+1
    246          except ValueError:
    247              wx.LogMessage("FuzzyScan went wrong")
    248              return ''
    249          return fulltext[left:right], left,right
    250
    251  #############################################################################
    252  # test function for this module: simply run the module as "main"
    253  # a text entry window will pop up. Write something, select arbitray
    254  # segmnts of text with the mouse, and then right click the selection
    255  # for options like encryption, decryption, and setting of passphrase
    256  #############################################################################
    257  if __name__ == '__main__':
    258          _ = lambda x:x
    259          app = wxPyWidgetTester(size = (400, 400))
    260          #show the login panel in a main window
    261          app.SetWidget(gmCryptoText, -1)
    262          app.MainLoop()
    263