#!/usr/bin/python3
# *-* coding:utf-8 *-*

#***************************************************************************
# This file is part of the CRYPTO BONE
# File     : cryptobone2  installed in /usr/bin
# Version  : 2.0 (ALL-IN-ONE)
# License  : BSD-3-Clause
# Date     : 29 May 2025
# Contact  : Please send enquiries and bug-reports to innovation@senderek.ie
#
#
# Copyright (c) 2015-2025
#	Ralf Senderek, Ireland.  All rights reserved. (https://senderek.ie)
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. All advertising materials mentioning features or use of this software
#    must display the following acknowledgement:
#	   This product includes software developed by Ralf Senderek.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND  ANY EXPRESS OR 
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE  IMPLIED WARRANTIES 
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
# ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
# POSSIBILITY OF SUCH DAMAGE.
#****************************************************************************

# cryptobone2 is the graphical user interface (GUI) for a secure messaging system
# that makes sure a user's message and attachment is always encrypted without burdening the user
# with complex message key management. Based on a separate daemon, both ease-of-use
# and security are assured by a novel approach to encryption key management.
#
# While the message keys are secured by a daemon running on the Linux machine,
# additional protection can be achieved by using an external device for storing
# the encryption keys. This external device can be another Linux computer dedicated
# to this task or a Raspberry Pi or any device that can install the CryptoBone software.
#
# The Crypto Bone secure messaging system automatically encrypts a message with
# a strong message key, stored in the cryptobone daemon's data base for secrets.
# Except for an initial registration of a message key for a recipient, the user
# does not need to remember any of these message keys being in use, as the system
# updates keys when messages are sent out or received.
#
# While only strongly end-to-end encrypted messages and attachments are sent out,
# via the safewebdrop transport mechanism, incoming messages that cannot be decrypted
# with registered message keys are discarded.
#
# In order to transfer encrypted messages the system has to be set up with a
# working safewebdrop account used for this purpose only. (see https://safewebdrop.com)


import os
import sys
import base64
import time

# debugging off
DEBUG_DISPLAY = False
DEBUG = False
#DEBUG_DISPLAY = True
#DEBUG = True

OS = os.name
Version = "2.0"

# find out askpass path
ASKPASS = "/usr/bin/systemd-ask-password"
if os.path.isfile ("/usr/libexec/openssh/gnome-ssh-askpass"):
     ASKPASS="/usr/libexec/openssh/gnome-ssh-askpass"
else:
     if os.path.isfile ("/usr/lib64/seahorse/seahorse-ssh-askpass"):
          ASKPASS="/usr/lib64/seahorse/seahorse-ssh-askpass"
     else:
          if os.path.isfile("/usr/lib/openssh/gnome-ssh-askpass"):
               ASKPASS = "/usr/lib/openssh/gnome-ssh-askpass"

	 

GUI = False

try:
    from tkinter import *
    from tkinter import font as tkFont
    GUI = True
except:
    print ("The GUI cannot be run. \nMaybe Tkinter is not installed?")
    sys.exit(4)

# STATUS   Allinone Finished Active
#              0        0       0	External device is not enabled
#              0        0       1	N/A
#	       0        1       0	External device is offline
#              0        1       1	External device works correctly
#              1        0       0	Allinone has no database and no running daemon
#              1        0       1	N/A
# 	       1        1       0	Allinone has a database but the daemon is not running
#              1        1       1	Allinone works correctly

Allinone = True
Finished = False
Active = False

TRANSPORT="WEBDROP"
Name="none"
StatusCount=3
MaxCount=0
Masterkey = False
MODE = "read"
MessageID = "None"
Recipient = "None"
Attachment = "None"
BONEIP=""

# GUI result of yes-no dialog
REPLY = False
CONTENT = ""

# GUI constants
BACKGROUND = "#ddd"
GRAY       = "#ccc"
LABEL      = "#eee"
GREEN      = "#cfc"
DEBUGCOLOR = "#aaa"
TAB        = "#ffc809"

INFO       = "#ddd"
ERRORCOLOR = "#800"
OKCOLOR    = "#080"
INFOCOLOR  = "#008"
KEYCOLOR   = "#efe"
YESCOLOR   = "#afa"
NOCOLOR    = "#faa"

#------------------------------------------------------------#
def unix (command) :
     if OS == "posix":
          Pipe = os.popen(command, "r")
          Result = Pipe.read()
          Pipe.close()
     return Result


#------------------------------------------------------------#
def askyesno(Header, Msg):
     global REPLY

     # create a pop-up Toplevel window to receive a choice in REPLY

     def returnyes():
          global REPLY
          REPLY = True
          dialog.destroy()
          return REPLY

     def returnno():
          global REPLY
          REPLY = False
          dialog.destroy()
          return REPLY

     dialog = Toplevel()
     dialog.title (Header)
     L = Label(dialog, text=Msg, font=Bold )
     ReplyFrame = Frame(dialog, bg=BACKGROUND, pady=5)
     Yes = Button(ReplyFrame, text= "  Yes  ", font=BFont, width=16, bg=YESCOLOR, command=returnyes)
     No  = Button(ReplyFrame, text= "  No   ", font=BFont, width=16, bg=NOCOLOR, command=returnno)

     L.pack(padx=30, pady=10)
     Yes.pack(padx=8, side=LEFT, pady=10)
     No.pack(padx=8, side=LEFT, pady=10)
     ReplyFrame.pack()
     dialog.transient(Window)
     try:
         dialog.grab_set()
     except:
         pass
     dialog.wait_window()

#------------------------------------------------------------#
def askinput(Header, Msg, Value):
     global CONTENT, REPLY

     # create a pop-up Toplevel window to receive a string in CONTENT and REPLY=True

     def returnyes():
          global REPLY, CONTENT
          CONTENT = Input.get()
          REPLY = True
          dialog.destroy()
          return REPLY

     def returnno():
          global REPLY, CONTENT
          CONTENT = Value
          REPLY = False
          dialog.destroy()
          return REPLY

     dialog = Toplevel()
     dialog.title (Header)
     L = Label(dialog, text=Msg, font=Bold )
     ReplyFrame = Frame(dialog, bg=BACKGROUND, pady=5)
     Input = Entry(master=ReplyFrame, width=30, font=Bold)
     Input.insert(0,Value)
     Yes = Button(ReplyFrame, text= "  Yes  ", font=BFont, width=16, bg=YESCOLOR, command=returnyes)
     No  = Button(ReplyFrame, text= "  No   ", font=BFont, width=16, bg=NOCOLOR, command=returnno)

     L.pack(padx=30, pady=10)
     Input.pack(padx=10, pady=10)
     Yes.pack(padx=8, side=LEFT, pady=10)
     No.pack(padx=8, side=LEFT, pady=10)
     ReplyFrame.pack()
     dialog.transient(Window)
     try:
         dialog.grab_set()
     except:
         pass
     dialog.wait_window()

#------------------------------------------------------------#
def clean ():
     Text.delete("0.0","end")

#------------------------------------------------------------#
def clear_error():
     ErrorFrame.configure(bg=INFO)
     ErrorLabel.configure(bg=INFO)
     ErrorLabel.configure(text="")
     Window.update_idletasks()

#------------------------------------------------------------#
def is_active():
     Result = send_command("STATUS")
     if "active" in Result:
          return True
     return False	  

#------------------------------------------------------------#
def format_error(e):
     E = ""
     i = 0
     for C in e :
          if C == '\n':
               E = E + C
               i = 0
          else:
               if i < 67 :
                    E = E + C
                    i += 1
               else: 
                    E = E + C +'\n'
                    i = 0
     return E

#------------------------------------------------------------#
def check_webdrop(user, server):
     Result = send_command("CHECKWEBDROP OUT " + user + " " + server)
     if (user and ("TESTMAIL sent" in Result)) :
           message = "Test message is sent out successfully.\n" + "Please be patient, as receiving the message may take a minute!"
           print(message)
           InfoPanel.configure(fg="darkgreen")
           InfoPanel.configure( text=format_error(message) ) 
           Window.update_idletasks()
     else:
           print("Test message cannot be sent out !")
           InfoPanel.configure(fg="red")
           InfoPanel.configure( text=format_error(Result) ) 
           Window.update_idletasks()
     unix("sleep 3")
     Result = send_command("CHECKWEBDROP IN")
     if ("unencrypted test email from root" in Result) :
           print("Test message received successfully")
           InfoPanel.configure(fg="darkgreen")
           InfoPanel.configure( text=format_error("Test message received successfully") ) 
           Window.update_idletasks()
     else:
           print("No webdrop received !")
           InfoPanel.configure(fg="red")
           InfoPanel.configure( text=format_error(Result) ) 
           Window.update_idletasks()
     print ()

#------------------------------------------------------------#
def get_initial_status():
     global Allinone, Finished, Active, BONEIP, ID, Name

     Message=""

     RESULT = send_command("GETALLINONE")
     print("GETALLINONE -> "+RESULT)
     if ("EXTERNAL" in RESULT):
          Allinone = False
          if ( "unfinished" in RESULT ):
               Active = False
               StatusLabel.configure(text="UNFINISHED")
               StatusLabel.configure(bg="orange")
               Message = "Please Copy External Secrets in SETUP"

          if ( "waiting" in RESULT ):
               print("Master key is not yet uploaded.")
               Active = False
               StatusLabel.configure(text="waiting ")
               StatusLabel.configure(bg="orange")
               BONEIP = RESULT[19:]
               IPLabel.configure(text=BONEIP)
               Message = "Please REBOOT the computer to upload the master key"

          elif ("active" in RESULT) :
	       # The flag UPLOADED has been set on the client machine
	       # tell if the external device is waiting or active by sending the STATUS command
               EXTSTATUS = send_command("STATUS")
               if "waiting" in EXTSTATUS :
                    Active = False
                    StatusLabel.configure(text="waiting")
                    StatusLabel.configure(bg="orange")
                    Message = "Please REBOOT the computer to upload the master key"
               if  "active" in EXTSTATUS:
                    Active = True
                    StatusLabel.configure(text="active")
                    StatusLabel.configure(bg="lightgreen")
               BONEIP = RESULT[19:]
               IPLabel.configure(text=BONEIP)
               if Name == "none":
                    Result = send_command("SETUP WID")
                    if not "failed" in Result:
                         ID.configure(text=Result[:-1])
                         Name=Result[:-1]
          
          elif ("offline" in RESULT) :
               Active = False
               StatusLabel.configure(text="OFFLINE")
               StatusLabel.configure(bg="yellow")
               BONEIP = RESULT[19:]
               IPLabel.configure(text=BONEIP)
               Message = "Please check the network on your external device."
          else:
               IPLabel.configure(text="EXTERNAL CRYPTO BONE")
               Finished = False 
               Active = False
               if ("0.0.0.0" in RESULT):
                     IPLabel.configure(text="0.0.0.0")
                     Message = "Please enter the IP address of your Cryptobone in SETUP."
	  
     elif ("ALLINONE" in RESULT):
          Allinone = True
          IPLabel.configure(text="ALL-IN-ONE")
          if ("active" in RESULT):
               ###Message = "Reading messages and attachments, please be patient!"
               Finished = True
               Active = True
               StatusLabel.configure(text="active")
               StatusLabel.configure(bg="lightgreen")
               if Name == "none":
                    Result = send_command("SETUP WID")
                    if not "failed" in Result:
                         ID.configure(text=Result[:-1])
                         Name=Result[:-1]
          elif ("nodatabase" in RESULT):
               Message =  "Please REBOOT!"
               Finished = False
               Active = False
               StatusLabel.configure(text="inactive")
               StatusLabel.configure(bg="yellow")
          if Name == "none":
               Result = send_command("SETUP WID")
               if not "failed" in Result:
                    ID.configure(text=Result[:-1])
                    Name=Result[:-1]
     
     Text.insert("end",Message)
     showinfo("",Message)
     ### print("Status: ",Allinone,Finished,Active)

#------------------------------------------------------------#
def contact():
     import subprocess

     Content = InputField.get().split(" ")
     if InputField.get() == '':
          Content = ["none"]
     args = ["sudo", "-A", "/usr/lib/cryptobone/cbcontrol"]
     for Word in Content:
          args.append(Word)
     ENV = os.environ.copy()	  
     ENV['SUDO_ASKPASS'] =  ASKPASS
     p = subprocess.Popen(args, bufsize=-1, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=False, env=ENV)
     if DEBUG_DISPLAY:
          print (InputField.get())
     (out,err) =  p.communicate()
     if err != None:
          print ("error:",str(err.decode('utf-8')))
     if DEBUG_DISPLAY:	   
          Text.insert("end",str(out.decode('utf-8')))
          Text.insert("end", "\n")

#------------------------------------------------------------#
def send_command(Command):
     import subprocess

     # do NOT sanitize input
     Content = Command.split(" ")	       
     args = ["sudo", "-A", "/usr/lib/cryptobone/cbcontrol"]
     for Word in Content:
          args.append(Word)
     ENV = os.environ.copy()	  
     ENV['SUDO_ASKPASS'] =  ASKPASS
     p = subprocess.Popen(args, bufsize=-1, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=False, env=ENV)
     if DEBUG_DISPLAY:
           print (Command)
     (out,err) =  p.communicate()
     if err != None:
           print ("error:",str(err.decode('utf-8')))
     if DEBUG_DISPLAY:	   
           print (out.decode('utf-8'))
           Text.insert("end",str(out.decode('utf-8')))
           Text.insert("end", "\n")
     return str(out.decode('utf-8'))

#------------------------------------------------------------#
def set_select_box():
     List = []
     if MODE == "write" or MODE == "key" :
          List = send_command("KEY RECIPIENTLIST").split("\n")
     if MODE == "read":
          List = send_command("READ MESSAGELIST").split("\n")
     if MODE == "setup":
          List = send_command("READ WEBDROPLIST").split("\n")
     SBox.delete(0,20)   
     Count = 0
     SBox.delete(0,END)
     for Item in List:
          if Item :
               if Item != "ssh key not available" :	  
                    SBox.insert(Count, str(Item))
          Count += 1

#------------------------------------------------------------#
def set_attach_box():
     List = []
     if MODE == "read":
          List = send_command("ATTACHMENT LIST").split("\n")
     AttachSBox.delete(0,20)   
     Count = 0
     AttachSBox.delete(0,END)
     for Item in List:
          if Item :
               if Item != "ssh key not available" :	  
                    AttachSBox.insert(Count, str(Item))
          Count += 1


#------------------------------------------------------------#
def set_read():
     global MODE, MessageID

     MODE = "read"
     Mode.configure(text="READ")
     L.configure(text="Messages")
     Key1.pack_forget()
     Setup1.pack_forget()
     AttachAdd.pack_forget()
     ConsoleButton.pack_forget()
     clean()
     TextFrame.grid(row=2, column=0)
     ControlFrame.grid(row=3, column=0)
     Button1.configure(text="Reply")
     Button2.configure(text="Check for new")
     Button3.configure(text="Destroy")
     Button1.pack(padx=8, side=LEFT)
     Button2.pack(padx=8, side=LEFT)
     Button3.pack(padx=8, side=RIGHT)
     if Active:
          SelectBox.pack()
          set_select_box()
          AttachBox.pack()
          set_attach_box()	 
     else:
          SelectBox.pack_forget()
          AttachBox.pack_forget()
     MessageID = "None"
     L1.configure(text=MessageID)
     L2.configure(text="NEW")
     RightFrame.grid(row=1, column=1, sticky=N, rowspan=2)
     clear_error()

#------------------------------------------------------------#
def set_write():
     global MODE, Recipient

     MODE = "write"
     Mode.configure(text="WRITE")
     L.configure(text="Recipients")
     ControlFrame.pack_forget()
     Key1.pack_forget()
     Setup1.pack_forget()
     ConsoleButton.pack_forget()
     AttachAdd.pack_forget()
     AttachBox.pack_forget()
     clean()
     TextFrame.grid(row=2, column=0)
     ControlFrame.grid(row=3, column=0)
     Button1.configure(text="Send Message")
     Button2.configure(text="Clear")
     Button1.pack(padx=8, side=LEFT)
     Button2.pack(padx=8, side=LEFT)
     Button3.pack_forget()
     if Active:
          set_select_box()
          SelectBox.pack()
          clear_attachment()
          AttachAdd.pack()
     Recipient = "None"
     L1.configure(text=Recipient)
     L2.configure(text="NEW")
     RightFrame.grid(row=1, column=1, sticky=N, rowspan=2)
     clear_error()

#------------------------------------------------------------#
def set_reply():
     global MODE
     MODE = "write"
     Mode.configure(text="WRITE")
     ControlFrame.pack_forget()
     Key1.pack_forget()
     Setup1.pack_forget()
     ConsoleButton.pack_forget()
     SelectBox.pack_forget()
     AttachBox.pack_forget()
     clean()
     TextFrame.grid(row=2, column=0)
     ControlFrame.grid(row=3, column=0)
     Button1.configure(text="Send Message")
     Button2.configure(text="Clear")
     Button3.pack_forget()
     if Active:
          clear_attachment()
          AttachAdd.pack()
     L1.configure(text="Recipient")
     L2.configure(text="REPLY")
     clear_error()
     RightFrame.grid(row=1, column=1, sticky=N, rowspan=2)
     ####clear_attachment()

#------------------------------------------------------------#
def set_key():
     global MODE
     MODE = "key"
     Mode.configure(text="KEYS")
     L.configure(text="Registered Contacts")
     SelectBox.pack_forget()
     AttachAdd.pack_forget()
     TextFrame.grid_forget()
     ControlFrame.grid_forget()
     DebugFrame.pack_forget()
     Setup1.pack_forget()
     ConsoleButton.pack_forget()
     AttachBox.pack_forget()
     Key1.pack()
     LeftFrame.grid(row=2, column=0, sticky=N, padx=10)
     if Active:
          set_select_box()
          SelectBox.pack()
     L1.configure(text="")
     L2.configure(text="")
     RightFrame.grid(row=1, column=1, sticky=N, rowspan=2)
     clear_error()

#------------------------------------------------------------#
def set_setup():
     global MODE, Allinone

     MODE = "setup"
     Mode.configure(text="SETUP")
     SelectBox.pack_forget()
     AttachAdd.pack_forget()
     TextFrame.grid_forget()
     ControlFrame.grid_forget()
     Key1.pack_forget()
     AttachBox.pack_forget()
     Setup1.pack()
     LeftFrame.grid(row=2, column=0, sticky=N, padx=10)
     ControlFrame.grid(row=3, column=0)
     Linkstatus = send_command("GETALLINONE")
     if "ALLINONE" in Linkstatus :
          Button1.configure(text="Use EXTERNAL")
          Button1.pack(padx=8, side=LEFT)
          Button2.pack_forget()
          Button3.pack_forget()
     else:
          Button3.pack_forget()
          Button1.configure(text="Use ALL-IN-ONE")
          Button2.configure(text="Copy EXT Secrets")
          Button3.configure(text=" Set EXT-BONEIP")
          Button1.pack(side=LEFT, padx=8)
          Button2.pack(side=LEFT, padx=8)
          Button3.pack(side=LEFT, padx=8)
     L1.configure(text="")
     L2.configure(text="")
     if Active:
          set_select_box()
          L.configure(text="INCOMING Safe Webdrops")
          SelectBox.pack()
          ConsoleButton.pack(side=BOTTOM, pady=100) 
     RightFrame.grid(row=1, column=1, sticky=N, rowspan=2)
     clear_error()

#------------------------------------------------------------#
def copy_external_secrets():
     BootDir = "/dev/shm/BOOT"          
     RES=unix("ls -a " + BootDir + " 2>/dev/null")
     if BootDir and RES:
          #  the RAMdisk exists, see what is in it
          RES=unix("ls "+BootDir+"/master.key 2>/dev/null")
          if RES:
               print("RES = "+ RES)
               # this device holds a master key
               print("FOUND master.key")
               print ("Copying external secrets from "+BootDir+" into the All-IN-ONE database.")      
               askyesno("Moving Keys from RAMDISK", "Copying external secrets from "+BootDir+" into the All-IN-ONE database.\n\nProceed?")
               if REPLY:
                    askyesno("Moving Keys from RAMDISK","You will now replace the keys that are used to access your EXTERNAL Crypto Bone.\n\nIf your EXTERNAL Crypto Bone is already working, you will DESTROY\n the keys used to access this working EXTERNAL Crypto Bone\n by replacing the old keys with new keys from the RAMDISK.\n\nOnce you have replaced the external keys, this action\n cannot be undone.\n\n You will continue with a new EXTERNAL Crypto Bone, if you proceed here.\n\nDo your really want to do that?\n\nThink twice.")
               if REPLY: 
                    RES = send_command("COPY_EXTERNAL_SECRETS "+BootDir)
                    if "success" in RES :
                         showinfo("Moving Keys from RAMDISK","Your Crypto Bone keys have been moved successfully to your computer.\nYou need to reboot this main computer in order to activate the new keys.")
                    elif "failed" in RES :
                         showinfo("Moving Keys from RAMDISK","The secrets in your RAMDISK have not been transferred completely. Please try again.")
                    elif "nokeys" in RES :
                         showinfo("Moving Keys from RAMDISK","There are no keys in " + BootDir)
 
          else:
               showinfo("Moving Secrets from RAMDISK","Cannot locate the external master.key in " + BootDir)
               return "no master.key"
               return "SUCCESS"
     else:
          showinfo("Moving Secrets from RAMDISK","There is no RAMDISK "+ BootDir + " where the external masterkey must be stored" )
          return "no RAMDISK"


     return "failed"
	  
#------------------------------------------------------------#
def action1():
    global Recipient, MessageID, Allinone, Name, StatusCount
    
    if MODE == "read":
         # reply to the selected message
         MESSAGE = Text.get("0.0","end").split("\n")
         
         set_reply()
         Text.insert("end","\n\n\n")
         for Line in MESSAGE:
               if Line:
                    Text.insert("end", "> "+Line+"\n")
         if len(MessageID) > 4 :
               while MessageID[len(MessageID)-1] != '.':
                     MessageID = MessageID[:-1]
               Recipient = MessageID[:-1]		   
         L1.configure(text=Recipient)     
         L2.configure(text="REPLY")     

    elif MODE == "write":
         MESSAGE = Text.get("0.0","end")
         # do NOT sanitize message, all characters allowed
         
         ENCODEDMESSAGE = MESSAGE.encode('utf-8')
         MESSAGE = base64.b64encode(ENCODEDMESSAGE).decode('utf-8')
         if Recipient != "None" and  Recipient != "Recipient":
              if Attachment == "None":
                   Result = send_command("WEBDROP "+Recipient+" "+MESSAGE+ " none")
                   print("GUI " +Result)		   
                   if ("WEBDROP MESSAGE SAVED" in Result) :
                        Text.insert("end", "\n\n"+Result)
                        showsuccess("Sending Message Out","Message is sent out successfully")
                   else:	   
                        showinfo("Sending Message Out","Message is not sent out!")
              else:
                   # send an attachment with the message
                   Result = send_command("WEBDROP "+Recipient+" " + MESSAGE + " " + Attachment)
                   print (Result)
                   print (Attachment)
                   if ("WEBDROP MESSAGE+ATTACHMENT SAVED" in Result) :
                        Text.insert("end", "\n\n"+Result)
                        showsuccess("Sending Message Out","Message and attachment are sent out successfully")
                   else:	   
                        if "Too Large" in Result:
                             Text.insert("end", "\n\n"+Result)
                             showerror("Sending Message Out","The attachment is too large and is not sent with the message!")
                        elif "ATTACHMENT EXCEEDS LIMIT" in Result:
                             Text.insert("end", "\n\n"+Result)
                             showerror("Sending Message Out","The attachment exceeds the limit the administrator has set on the server!")
         else:     
              showinfo("Missing Information","Please select a recipient from the menu before sending the message")

    elif MODE == "key":
        print ("action one key not implemented")

    elif MODE == "setup":
        if not Allinone:
             Allinone = True
             Finished = True
             Button1.configure(text="Use EXTERNAL")
             IPLabel.configure(text="ALL-IN-ONE")
             send_command("USEALLINONE")
        else:
             Allinone = False
             Button1.configure(text="Use ALL-IN-ONE")
             IPLabel.configure(text="EXTERNAL CRYPTO BONE")
             send_command("USECRYPTOBONE")
        StatusCount = 100
        Name = "none"
        ID.configure(text="")
        time.sleep(2)
        get_initial_status()
        
#------------------------------------------------------------#
def action2():
    global Recipient, MessageID

    if MODE == "read":
         # check for new messages
         send_command("FETCH")
         set_attach_box()	 
         set_select_box()

    elif MODE == "write":
         # forget message
         clean()
         clear_attachment()
         MessageID = "none"
         L1.configure(text=Recipient)
         L2.configure(text="NEW")

    elif MODE == "key":
         print ("action two key not implemented")
   
    elif MODE == "setup":
         print ("NOW COPY EXTERNAL SECRETS")
         copy_external_secrets()

#------------------------------------------------------------#
def action3():
     global BONEIP     
     if MODE == "read":
          select()
          # destroy selected message
          if MessageID :
               askyesno("Destroy Message", "Do you really want to destroy this message: "+MessageID)
               if REPLY:
                    send_command("READ DESTROY "+MessageID)
                    clean()
                    set_select_box()

     elif MODE == "setup":
          # update EXTERNAL BONEIP address
          askinput("Update External Bone IP Address", "Do you want to update the current IP address\n for your EXTERNAL Crypto Bone? " , str(BONEIP) )
          if REPLY:
               if CONTENT :
                    print ("UPDATE BONEIP = " + CONTENT)
                    send_command("BONEIP " + CONTENT)
                    IPLabel.configure(text=CONTENT)

#------------------------------------------------------------#
def select():
    global MessageID, Recipient

    index = 0
    if len(SBox.curselection()) > 0:
         index = SBox.curselection()[0]
    Selection = SBox.get(index)
    L1.configure(text=Selection)
    if MODE == "read":
        MessageID = Selection
        if MessageID:
             MESSAGE = send_command("READ MESSAGE "+MessageID)
             if MESSAGE:
                  clean()
                  try:
                       Plaintext = base64.b64decode(MESSAGE).decode('utf-8')
                       Text.insert("end",Plaintext)
                  except:
                       pass		  
    elif MODE == "write":
        Recipient = Selection
    elif MODE == "setup":
        Setup1.pack_forget()
        Button1.pack_forget()
        Button2.pack_forget()
        Button3.pack_forget()
        LeftFrame.grid_forget()
        TextFrame.grid(row=2, column=0)
        Recipient = Selection
        MessageID = Selection
        MESSAGE = send_command("READ WEBDROP " + MessageID)
        if MESSAGE:
             clean()
             Plaintext = base64.b64decode(MESSAGE)
             Text.insert("end",Plaintext)

#------------------------------------------------------------#
def attach() :
     global Attachment
 
     empty = False
     FileName = AttachFilename.get()
     if FileName == "":
         empty = True
     FileName = os.path.expanduser("~/" + FileName)
     if os.path.isfile( FileName ) :
         AttachFilename.configure(bg="lightgreen")
         showinfo( "", FileName + " will be attached to your message." )
         Attachment = FileName
     else:
         Attachment = "None"
         AttachFilename.configure(bg="white")
         if (not empty) :
               showerror( "", FileName + " : does not exist" )

#------------------------------------------------------------#
def clear_attachment():
     global Attachment
     Attachment = "None"
     AttachFilename.delete(0,END)
     AttachFilename.configure(bg="white")

#------------------------------------------------------------#
def attachment_copy():
    index = 0
    if len(AttachSBox.curselection()) > 0:
        index = AttachSBox.curselection()[0]
    Filename = AttachSBox.get(index)
    if MODE == "read" and Filename:
        askyesno("","Do you want to copy " + Filename + " into your home directory?\nExisting files in $HOME/.safewebdrop will be overwritten!")
        if REPLY :
             RES = send_command( "ATTACHMENT COPY " + Filename )
             if ("SAVED" in RES) :
                  showsuccess("", Filename + " has been copied into the directory .safewebdrop")
             else:		  
                  showerror("", Filename + " cannot be copied into the directory .safewebdrop")

             set_attach_box()

#------------------------------------------------------------#
def message_copy():
    index = 0
    if len(SBox.curselection()) > 0:
        index = SBox.curselection()[0]
    Messagename = SBox.get(index)
    if MODE == "read" and Messagename:
        askyesno("","Do you want to copy " + Messagename + " into your home directory?\nThis may reduce the protection of your clear text message.\nExisting files in $HOME/.safewebdrop will be overwritten!")
        if REPLY :
             RES = send_command( "MESSAGE COPY " + Messagename )
             print(RES)
             if ("SAVED" in RES) :
                  showsuccess("", Messagename + " has been copied into the directory .safewebdrop")
             else:		  
                  showerror("", Messagename + " cannot be copied into the directory .safewebdrop")

             set_attach_box()

#------------------------------------------------------------#
def attachment_delete():
    index = 0
    if len(AttachSBox.curselection()) > 0:
        index = AttachSBox.curselection()[0]
    Filename = AttachSBox.get(index)
    if MODE == "read" and Filename:
        askyesno("","Do you want to delete " + Filename + "? You can copy this attachment before you destroy it")
        if REPLY :
             RES = send_command( "ATTACHMENT DELETE " + Filename )
             if ("DELETED" in RES) :
                  showsuccess("", Filename + " has been deleted.")
                  set_attach_box()

#------------------------------------------------------------#
def is_message_key(NewKey):
     if NewKey == "none":
          return False
     if len(NewKey) > 19:
          return True
     return False

#------------------------------------------------------------#
def register():
     if not is_active():
         showerror("", "Your Crypto Bone is not active! ")
         return "failed"
     K = InitKey.get()
     ID = Address.get()
     if (K == "none") or (K == "delete") or (K == "destroy"):
          K = "00000000000000000000" 
     if "@" in ID :
          showerror('Contact Registration', "This is an EMAIL ID, please use % instead of @")
     elif is_message_key(K) :
          # TODO !!! : check if email address is stored already

          List = []
          List = send_command("KEY RECIPIENTLIST").split("\n")
          if ID in List:
               askyesno("Update A Key?","The key for "+ID+" is already in use.\n\n If you wish to reset this key, you can set a new initial secret.\nYour contact needs to do the same to continue the conversation.\n\nIf you wish to delete this contact, then you can register the key \"none\"\n\nDo you wish to proceed with the key update?\n")
               if not REPLY:
	            # abort 
                    return "nothing done"
	  # KEY USE registers without checking
          RES = send_command("KEY USE "+ ID +" "+K)
          if "failed" in RES :
               showerror('Contact Registration', RES[8:])
          if "deleted" in RES :
               showsuccess('Deleted Contact', RES)
          if "success" in RES :     
               showsuccess('Contact Registration', ID + "\nis now registered and can be used.")
          unix("sleep 3")	       
          set_key()
     else:
          showerror('Contact Registration', "The message key is too short.")

#------------------------------------------------------------#
def show_webdrop_setup():
     Result = send_command("SETUP WEBDROP")
     if Result:
          List = Result.split("\n")
          for Line in List:
               Secretvalue = Line.split(":")
               if Secretvalue[0] == "webdropserver":
                    Hostname.delete(0,END)		       
                    Hostname.insert(0,Secretvalue[1])		       
               if Secretvalue[0] == "webdropuser":
                    Username.delete(0,END)		       
                    Username.insert(0,Secretvalue[1])		 
	       # never show the webdropsecret	    
               CODE=send_command("SETUP REGISTRATION")
               RegistrationCode.delete(0,END)           
               RegistrationCode.insert(0,CODE[:-1])           
     InfoPanel.configure(text="")

#------------------------------------------------------------#
def webdrop_setup():

     H = Hostname.get()
     U = Username.get()
     if H :
          send_command("SETUP WEBDROPSERVER "+H)
     if U :
          send_command("SETUP WEBDROPUSER "+U)
     InfoPanel.configure( text="" ) 
     Window.update_idletasks()
     check_webdrop(U,H)

#------------------------------------------------------------#
def register_webdrop_account():

     CODE=send_command("SETUP REGISTRATION")
     RegistrationCode.delete(0,END)           
     RegistrationCode.insert(0,CODE[:-1])           
     print ("Registration")
     askyesno("New Safe Webdrop Account Registration", "Do you really want to register a new Safe Webdrop account?\n This will destroy any Safe Webdrop account you have registered before!\n\nYou need to enter your Safe Webdrop ID and the Safe Webdrop server name in the setup above.\nIf your registration goes through you will find a different registation code below.\nYou must show the new registration code to your Safe Webdrop administrator in order to enable your new account.")
     if REPLY:
           Result = send_command("SETUP CLEARREG")
           show_webdrop_setup()
           CODE=send_command("SETUP REGISTRATION")
           RegistrationCode.delete(0,END)           
           RegistrationCode.insert(0,CODE[:-1])           
           askyesno("New Safe Webdrop Account Registration", "Is the setup information (webdrop server and ID) korrect?")
           if REPLY:
                 Result = send_command("SETUP REGISTER")
                 CODE=send_command("SETUP REGISTRATION")
                 RegistrationCode.delete(0,END)           
                 RegistrationCode.insert(0,CODE[:-1])           

#------------------------------------------------------------#
def terminate_GUI():
     Window.destroy()     

#------------------------------------------------------------#
def toggle_console():
     global DEBUG, DEBUG_DISPLAY, DebugFrame

     DEBUG = not DEBUG
     DEBUG_DISPLAY = not DEBUG_DISPLAY
     if DEBUG:
          if MODE == "read" or MODE == "write" or MODE == "setup" : 
               DebugFrame.grid(row=5, column=0, columnspan=2)
     else:
          DebugFrame.grid_forget()

#------------------------------------------------------------#
def readuser():
     USER = InputField.get()
     clear_error()
     # check if this is a valid user
     if USER and (USER in unix("grep \"^"+USER+":\" /etc/passwd")):
          RET=unix("/usr/bin/pkexec /usr/bin/activate-cryptobone " + USER)
          print (RET)
          if RET == "success":
               showsuccess( "SUCCESS","Your Crypto Bone can now be used.\nPlease reboot your computer to create the secrets that are missing.")
               unix("sleep 10")
               sys.exit(0)
          if RET == "initialised":
               showerror( "ERROR","This Crypto Bone is already set up.\nYour Crypto Bone Daemon is not enabled.\nTry: systemctl enable cryptoboned")
               unix("sleep 10")
               sys.exit(1)
          if RET == "nosuchuser":
               showerror( "ERROR","No such user. Please try again.")
          if RET == "noparameter":
               showerror( "ERROR","You must specify a user name.")
     else:
          showerror( "ERROR","No such user. Please try again.")
	  
#------------------------------------------------------------#
def showinfo(Title, Text):
     ErrorFrame.configure(bg=INFO)
     ErrorLabel.configure(text=Text)
     ErrorLabel.configure(fg=INFOCOLOR, bg=INFO)
     Window.update_idletasks()

#------------------------------------------------------------#
def showsuccess(Title, Text):
     ErrorFrame.configure(bg=INFO)
     ErrorLabel.configure(text=Text)
     ErrorLabel.configure(fg=OKCOLOR, bg=INFO)
     Window.update_idletasks()

#------------------------------------------------------------#
def showerror(Title, Text):
     ErrorFrame.configure(bg=INFO)
     ErrorLabel.configure(text="ERROR: "+Text)
     ErrorLabel.configure(fg=ERRORCOLOR, bg=INFO)
     Window.update_idletasks()

#------------------------------------------------------------#
def OpenHelpUrl():
     import webbrowser
     webbrowser.open_new("https://crypto-bone.com/help2")
     
#------------------------------------------------------------#
def OpenHomeUrl():
     import webbrowser
     webbrowser.open_new("https://crypto-bone.com")
     
#------------------------------------------------------------#
def HelpPage():
     import webbrowser
     if MODE == "read" :
          webbrowser.open_new("https://crypto-bone.com/help2/read")
     elif MODE == "write" :
          webbrowser.open_new("https://crypto-bone.com/help2/write")
     elif MODE == "key" :
          webbrowser.open_new("https://crypto-bone.com/help2/keys")
     elif MODE == "setup" :
          webbrowser.open_new("https://crypto-bone.com/help2/setup")
     else :
          webbrowser.open_new("https://crypto-bone.com/help2")
     

###############################################################
# Main
###############################################################

if GUI:
     Window = Tk()

     # this work-around fixes a bug introduced with fractional scaling in Gnome,
     # as the system scaling factor is not active on first use after login
     # and causes the window being too small
     Window.tk.call("tk","scaling",2.5)


     Window.title("Crypto Bone Control 2.0")

     MainFrame    = Frame(Window,    bg=BACKGROUND)
     DebugFrame   = Frame(MainFrame, bg=DEBUGCOLOR, pady=5)
     ErrorFrame   = Frame(MainFrame, bg=BACKGROUND, pady=10)
     HeadFrame    = Frame(MainFrame, bg=BACKGROUND, padx=0, pady=0)
     StatusFrame  = Frame(HeadFrame, bg=BACKGROUND, padx=0, pady=5)
     ModeFrame    = Frame(HeadFrame, bg=BACKGROUND, padx=0, pady=5)
     LeftFrame    = Frame(MainFrame, bg=BACKGROUND, padx=5, pady=2)
     TopFrame     = Frame(MainFrame, bg=BACKGROUND, padx=10, pady=10)
     TextFrame    = Frame(MainFrame, bg=BACKGROUND)
     ControlFrame = Frame(MainFrame, bg=BACKGROUND, pady=5)
     RightFrame   = Frame(MainFrame, bg=BACKGROUND, padx=15, pady=2)
     DisplayFrame = Frame(LeftFrame, bg=BACKGROUND, pady=2)
     

     Big = tkFont.Font(family="utopia", size=16)
     Title = tkFont.Font(family="utopia", size=12)
     Info  = tkFont.Font(family="arial", size=10, slant="italic")
     Bold  = tkFont.Font(family="arial", size=11, weight="bold")
     BFont = tkFont.Font(family="utopia", size=12, weight="normal")
     TextFont = tkFont.Font(family="arial", size=11, weight="normal")
     MonoFont = tkFont.Font(family="monospace", size=11, weight="normal")
     
     # ERROR frame
     ErrorLabel = Label(ErrorFrame, text="checking the status ...", font=Bold,  bg=BACKGROUND)
     ErrorLabel.pack(side=LEFT, padx=10)

     # DEBUG frame
     ConsoleLabel = Label(DebugFrame, text="Console", font=Bold, width=16, bg=DEBUGCOLOR)
     CleanButton = Button(master=DebugFrame, text="Clean", command=clean)
     InputField = Entry(master=DebugFrame, font=Bold, width=56) 
     ExecButton = Button(master=DebugFrame, text="Send Command", command=contact)
     ConsoleLabel2 = Label(DebugFrame, text="", font=Bold, width=10, bg=DEBUGCOLOR)
     ConsoleLabel.pack(side=LEFT, pady=5)
     CleanButton.pack(side=LEFT) 
     InputField.pack(padx=20,side=LEFT) 
     ExecButton.pack(side=LEFT) 
     ConsoleLabel2.pack(side=LEFT, padx=0)

     TransportLabel = Label(MainFrame, text="  Transport: SAFE WEBDROP  ", font=Bold, height=1, bg=BACKGROUND)
     

     # Logo image
     CBS_btn_img = PhotoImage(file='/usr/share/icons/default/logo-cryptobone-safewebdrop.png')
     CBSButton = Button(HeadFrame, image=CBS_btn_img, bg=GRAY,  width=180, height=150)
     CBSButton.bind("<Button-1>",lambda c: OpenHomeUrl())
     CBSButton.bind("<Enter>", lambda c: CBSButton.configure(bg="#eee"))
     CBSButton.bind("<Leave>", lambda c: CBSButton.configure(bg=BACKGROUND))

     # Status
     BoneLabel = Label(StatusFrame, text="CRYPTO BONE " + Version + " ", font=Big, width=18, bg=BACKGROUND)
     BoneLabel.bind("<Button-1>",lambda e: OpenHelpUrl())
     BoneLabel.bind("<Enter>", lambda e: BoneLabel.configure(bg=BACKGROUND))
     BoneLabel.bind("<Leave>", lambda e: BoneLabel.configure(bg=BACKGROUND))
     IPLabel = Label(StatusFrame, text="", width=22, height=1, font=Bold, bg="#ccc")
     StatusLabel = Label(StatusFrame, text="cut off", font=Bold, width=12, height=1, bg=TAB)
     ID = Label(MainFrame, text="", font=Bold, width=30, height=1, bg="#ccc")
     CBSButton.pack(padx=10, side=LEFT, pady=0) 
     BoneLabel.pack(padx=30, side=LEFT)
     IPLabel.pack(side=LEFT)
     StatusLabel.pack(side=LEFT)


     # Labels in TopFrame
     Mode = Label(TopFrame, text="READ", font=Bold, bg=TAB, width=11)
     L1 = Label(TopFrame, text="None", font=Bold, width=55, bg=LABEL)
     L2 = Label(TopFrame, text="None", font=Bold, width=10, bg=LABEL)
     Mode.pack(side=LEFT, padx=0)
     L1.pack(side=LEFT, padx=0)
     L2.pack(side=LEFT, padx=0)

     # TextFrame
     Scroll  = Scrollbar(TextFrame)
     Text    = Text(TextFrame, width=75, height=23, font=TextFont, borderwidth=8, relief=FLAT, yscrollcommand = Scroll.set)
     Scroll.pack( side = RIGHT, fill=Y )
     Text.pack()
     Scroll.config( command = Text.yview )

     # ModeFrame 
     ReadButton = Button(master=ModeFrame, text="READ", bg=GRAY, font=BFont, command=set_read)
     WriteButton = Button(master=ModeFrame, text="WRITE", bg=GRAY, font=BFont, command=set_write)
     KeyButton = Button(master=ModeFrame, text="KEYS", bg=GRAY, font=BFont, command=set_key)
     SetupButton = Button(master=ModeFrame, text="SETUP", bg=GRAY, font=BFont, command=set_setup)
     Help_btn_img = PhotoImage(file='/usr/share/icons/default/question-mark.png')
     HelpButton = Button(master=ModeFrame, image=Help_btn_img, bg=GRAY,  width=60, height=60,  command=HelpPage)
     ReadButton.pack(padx=20, side=LEFT, pady=5)
     WriteButton.pack(padx=20, side=LEFT, pady=5)
     KeyButton.pack(padx=20, side=LEFT, pady=5)
     SetupButton.pack(padx=20, side=LEFT, pady=5)
     HelpButton.pack(padx=20, side=LEFT, pady=5) 


     # ControlFrame
     Button1 = Button(master=ControlFrame, text="", bg=GRAY, font=BFont, width=16, command=action1)
     Button2 = Button(master=ControlFrame, text="", bg=GRAY, font=BFont, width=16, command=action2)
     Button3 = Button(master=ControlFrame, text="", bg=GRAY, font=BFont, width=16, command=action3)
     Button1.pack(padx=8, pady=5, side=LEFT)
     Button2.pack(padx=8, pady=5, side=LEFT)
     Button3.pack(padx=8, pady=5, side=LEFT)

     
     # RightFrame
     
     ### Select Box
     SelectBox = Frame(RightFrame, bg=BACKGROUND)
     L = Label(SelectBox, text="", bg=TAB,font=Bold,  width=30)
     L.pack()
     BoxFrame = Frame(master=SelectBox)
     XScroll  = Scrollbar(BoxFrame, orient=HORIZONTAL)
     SBox = Listbox(BoxFrame, height=10, width=29, font=TextFont, borderwidth=5, relief=FLAT, xscrollcommand = XScroll.set)
     XScroll.config(command=SBox.xview)
     XScroll.pack(side=BOTTOM, fill=X )
     SBox.pack()
     BoxFrame.pack(pady=10)
     SelectButton = Button(master=SelectBox, text="Select", bg=GRAY, font=BFont, command=select)
     SBox.bind('<Double-Button-1>', lambda e: message_copy())
     SelectButton.pack(pady=0)
    
     # make space
     FillRight = Label(SelectBox, text=" ", height=1, width=1, bg=BACKGROUND)
     FillRight.pack(pady=5)
     
     ### Attachment Box
     AttachBox = Frame(RightFrame, bg=BACKGROUND)
     AttachBoxFrame = Frame(master=AttachBox)
     LAttach = Label(AttachBoxFrame, text="Attachments", bg=TAB, font=Bold, width=30)
     AttachXScroll  = Scrollbar(AttachBoxFrame, orient=HORIZONTAL)
     AttachSBox = Listbox(AttachBoxFrame, height=4, width=29, font=TextFont, borderwidth=5, relief=FLAT, xscrollcommand = XScroll.set)
     AttachSBox.bind('<Double-Button-1>', lambda e: attachment_copy())
     AttachXScroll.config(command=AttachSBox.yview)
     AttachXScroll.pack(side=BOTTOM, fill=X )
     LAttach.pack()
     AttachBoxFrame.pack(pady=10)
     AttachSBox.pack()
     AttachSelectButton = Button(master=AttachBox, text="Delete", bg=GRAY, font=BFont, command=attachment_delete)
     AttachSelectButton.pack(pady=0)
     
     ### Attachment Select Box
     AttachAdd = Frame(RightFrame, bg=BACKGROUND)
     AttachAddInfo = Label(AttachAdd, text="Send this file in your \nhome directory securely:", height=2, fg="black", font=Bold, width=30, bg=BACKGROUND)
     AttachAddInfo.pack()
     AttachLabel = Label(AttachAdd, text="Attachment", bg=TAB, font=Bold, width=30)
     AttachLabel.pack(pady=5)
     AttachFilename = Entry(AttachAdd, font=Bold, width=30)
     AttachFilename.pack()
     AttachButton = Button(master=AttachAdd, text="Add File", bg=GRAY, font=BFont, command=attach)
     AttachButton.pack(pady=10)

     if Active:
          SelectBox.pack()
          AttachBox.pack()

     # Key Management
     Key1 = Frame(LeftFrame, height=110, pady=10, bg=BACKGROUND)
     KL1 = Label(Key1, text="Register a New Contact", font=Title, bg=TAB, width=30)

     # first Key frame 
     SubKL1 = Label(Key1, text="S-Webdrop-ID % Server", font=Bold, bg="white", width=27)
     SubKL2 = Label(Key1, text="Initial Secret", font=Bold, bg="white", width=27)
     KL1Info = Label(Key1, text="            Enter an initial secret for a new contact SafeWebdrop ID that is not yet registered           ", font=Info, bg=BACKGROUND)
     Address   = Entry(Key1, font=Bold, width=40) 
     InitKey = Entry(Key1, font=Bold, width=40) 
     K1Button = Button(master=Key1, text="Register", bg=GRAY, font=BFont, command=register)
     
     KL1.grid(row=0, column=0, columnspan=2)
     KL1Info.grid(row=1, column=0, columnspan=2, padx=3, pady=8)
     SubKL1.grid(row=2, column=0, padx=3, pady=2)
     Address.grid(row=2, column=1, padx=3, pady=2)
     SubKL2.grid(row=3,column=0, padx=3, pady=2)
     InitKey.grid(row=3, column=1, padx=3, pady=2)
     K1Button.grid(row=4, column=0, columnspan=2, padx=3, pady=15)
    

     # Setup frame
     Setup1 = Frame(LeftFrame, height=100, pady=10, bg=BACKGROUND)

     SL1 = Label(Setup1, text="Setup the SAFE WEBDROP Server for Message Exchange", font=Title, bg=TAB, width=50)
     InfoPanel = Label(Setup1, text="" , width=60, height=8, bg=BACKGROUND, fg="red", pady=5, justify="left")
     SubSL1 = Label(Setup1, text="Safe Webdrop Server Name", font=Bold, bg="white", width=27)
     SubSL2 = Label(Setup1, text="Safe Webdrop User ID", font=Bold, bg="white", width=27)
     SL1Info = Label(Setup1, text="Please enter your SAFE WEBDROP address  (no spaces) and give it to your contacts", font=Info, padx=10)
     Hostname = Entry(Setup1, width=40, font=Bold) 
     Username = Entry(Setup1, width=40, font=Bold) 
     S1Button = Button(master=Setup1, text="Update SAFE WEBDROP Setup", bg=GRAY, font=BFont, command=webdrop_setup)
     ShowButton = Button(master=Setup1, text="Show Setup", bg=GRAY, font=BFont, command=show_webdrop_setup)
     # Space
     Space = Label(Setup1, height=3)

     # setup 2
     SL2 = Label(Setup1, text="Registration of a new Safe Webdrop account (if you don't have one already)", font=Title, bg=TAB, width=66)
     SL2Info = Label(Setup1, text="In order to send safe webdrops you need a registered account on your safe webdrop server", font=Info, padx=10)
     SubSL5 = Label(Setup1, text="Registration code", font=Bold, bg="white", width=27)
     RegistrationCode = Entry(Setup1, width=28, font=Bold ) 
     REGButton  = Button(master=Setup1, text="Registration", bg=GRAY, font=BFont, command=register_webdrop_account)

     SL1.grid(row=0, column=0, columnspan=2, sticky=W, padx=10, pady=8)
     SL1Info.grid(row=1, column=0, columnspan=2, padx=10, pady=8, sticky=W)
     SubSL1.grid(row=2, column=0, padx=3, pady=2)
     Hostname.grid(row=2, column=1, padx=3, pady=2, sticky=W)
     SubSL2.grid(row=3,column=0, padx=3, pady=2)
     Username.grid(row=3, column=1, padx=3, pady=2, sticky=W)
     ShowButton.grid(row=4, column=0,  padx=3, pady=15)
     S1Button.grid(row=4, column=1,  padx=3, pady=15)

     Space.grid(row=5, column=0)
     
     SL2.grid(row=6, column=0, columnspan=2, sticky=W, padx=10, pady=8)
     SL2Info.grid(row=7, column=0, columnspan=2, padx=10, pady=8, sticky=W)
     SubSL5.grid(row=8, column=0, padx=3, pady=2)
     RegistrationCode.grid(row=8, column=1, padx=3, pady=2, sticky=W)
     REGButton.grid(row=9, column=1,  padx=3, pady=15)

     
     QuitButton = Button(master=MainFrame, text="Exit!", bg=GRAY, font=BFont, command=terminate_GUI, width=14)
     ConsoleButton = Button(master=RightFrame, text="Console", bg=GRAY, font=BFont, command=toggle_console, width=14)
     
     
     
     if not os.path.islink("/etc/systemd/system/multi-user.target.wants/cryptoboned.service"):
          AdminFrame = Frame(Window)
          AdminFill = Label(AdminFrame, text="",  height=2)
          AdminLabel = Label(AdminFrame, text="You want to activate your Crypto Bone?", width=60, height=1, font=Bold, bg="#eeffee")
          Text1 = Label(AdminFrame, text="Please input the login name of the user that\n will be using the Crypto Bone:")
          Text2 = Label(AdminFrame,text="This user will be listed in the sudoers file.")
          ExecButton = Button(master=AdminFrame, text=" Enable this user ", bg=GREEN, font=BFont, command=readuser)
          InputField = Entry(master=AdminFrame, width=15) 
          AdminFill2= Label(AdminFrame, text="",  height=1)
          AdminFill.pack()
          AdminLabel.pack(pady=10)
          Text1.pack(pady=5)
          Text2.pack(pady=5)
          InputField.pack(pady=10) 
          ExecButton.pack() 
          AdminFill2.pack()

          AdminFrame.pack()
          ErrorFrame.pack(fill=X)
          clear_error()
          Window.mainloop()


     # pack frames
     StatusFrame.pack()
     ModeFrame.pack()

     # set the main grid 
     
     # set row 2 to xxx pixels
     MainFrame.rowconfigure(2, minsize=810)

     HeadFrame.grid(row=0, column=0, sticky=NW)
     ID.grid(row=0, column=1, sticky=N, padx=10, pady=10)
     TransportLabel.grid(row=0, column=1, padx=10, pady=20)
     
     StatusFrame.pack()
     ModeFrame.pack()
     
     TopFrame.grid(row=1, column=0, sticky=NW)
     TextFrame.grid(row=2, column=0, sticky=N, padx=10)
     QuitButton.grid(row=3, column=1, pady=10)
     ErrorFrame.grid(row=4, column=0, columnspan=2)
     MainFrame.pack(padx=5, fill=X)
     if DEBUG:
          DebugFrame.grid(row=5, column=0, columnspan=2)
     
     Name = "none"
     
     RESULT = unix("pidof /usr/lib/cryptobone/cryptoboned")
     if "x"+RESULT == "x" :
          # crypto bone daemon is not running
          showerror('Crypto Bone Daemon failed', "You need to reboot your computer because the Crypto Bone Daemon is not running.")
          unix("sleep 5")
          sys.exit(2)
     Window.update_idletasks()

     get_initial_status()
     Window.mainloop()

#------------------------------------------------------------#
