From e6904fac14c0ba4f0a1340d99f8d6e3561ec8ff2 Mon Sep 17 00:00:00 2001
From: Pierre LALET <pierre.lalet@cea.fr>
Date: Tue, 12 Sep 2017 10:27:03 +0200
Subject: [PATCH] Add a fancy prompt

---
 scapy/config.py |   1 +
 scapy/main.py   | 190 +++++++++++++++++++++++++++++++++++-------------
 scapy/themes.py |   8 ++
 3 files changed, 147 insertions(+), 52 deletions(-)

diff --git a/scapy/config.py b/scapy/config.py
index 871c182c..8c4c60e9 100755
--- a/scapy/config.py
+++ b/scapy/config.py
@@ -449,6 +449,7 @@ debug_tls:When 1, print some TLS session secrets when they are computed.
     contribs = dict()
     crypto_valid = isCryptographyValid()
     crypto_valid_advanced = isCryptographyAdvanced()
+    fancy_prompt = True
 
 
 if not Conf.ipv6_enabled:
diff --git a/scapy/main.py b/scapy/main.py
index fda7e968..354816c6 100644
--- a/scapy/main.py
+++ b/scapy/main.py
@@ -9,20 +9,37 @@ Main module for interactive startup.
 
 from __future__ import absolute_import
 from __future__ import print_function
-import os,sys
+
+
 import glob
-import types
 import gzip
-import scapy.modules.six as six
 import importlib
-ignored = list(six.moves.builtins.__dict__.keys())
+import os
+from random import choice
+import sys
+import types
+
 
+import scapy.modules.six as six
 from scapy.error import *
 
+
+ignored = list(six.moves.builtins.__dict__.keys())
+
+
 LAYER_ALIASES = {
     "tls": "tls.all"
 }
 
+QUOTES = [
+    ("Craft packets like it is your last day on earth.", "Lao-Tze"),
+    ("Craft packets like I craft my beer.", "Jean De Clerck"),
+    ("Craft packets before they craft you.", "Socrate"),
+    ("Craft me if you can.", "IPv6 layer"),
+    ("To craft a packet, you have to be a packet, and learn how to swim in the "
+     "wires and in the waves.", "Jean-Claude Van Damme"),
+]
+
 def _probe_config_file(cf):
     cf_path = os.path.join(os.path.expanduser("~"), cf)
     try:
@@ -270,6 +287,29 @@ def scapy_delete_temp_files():
         except:
             pass
 
+def _prepare_quote(quote, author, max_len=78):
+    """This function processes a quote and returns a string that is ready
+to be used in the fancy prompt.
+
+    """
+    quote = quote.split(' ')
+    max_len -= 6
+    lines = []
+    cur_line = []
+    def _len(line):
+        return sum(len(elt) for elt in line) + len(line) - 1
+    while quote:
+        if not cur_line or (_len(cur_line) + len(quote[0]) - 1 <= max_len):
+            cur_line.append(quote.pop(0))
+            continue
+        lines.append('   | %s' % ' '.join(cur_line))
+        cur_line = []
+    if cur_line:
+        lines.append('   | %s' % ' '.join(cur_line))
+        cur_line = []
+    lines.append('   | %s-- %s' % (" " * (max_len - len(author) - 5), author))
+    return lines
+
 def scapy_write_history_file(readline):
     from scapy import utils
     if conf.histfile:
@@ -298,14 +338,102 @@ def interact(mydict=None,argv=None,mybanner=None,loglevel=20):
     console_handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
     log_scapy.addHandler(console_handler)
 
-    the_banner = "Welcome to Scapy (%s)"
-    if mybanner is not None:
-        the_banner += "\n"
-        the_banner += mybanner
+    conf.color_theme = DefaultTheme()
+
+    STARTUP_FILE = DEFAULT_STARTUP_FILE
+    PRESTART_FILE = DEFAULT_PRESTART_FILE
+
+    session_name = None
 
     if argv is None:
         argv = sys.argv
 
+    try:
+        opts = getopt.getopt(argv[1:], "hs:Cc:Pp:d")
+        for opt, parm in opts[0]:
+            if opt == "-h":
+                _usage()
+            elif opt == "-s":
+                session_name = parm
+            elif opt == "-c":
+                STARTUP_FILE = parm
+            elif opt == "-C":
+                STARTUP_FILE = None
+            elif opt == "-p":
+                PRESTART_FILE = parm
+            elif opt == "-P":
+                PRESTART_FILE = None
+            elif opt == "-d":
+                conf.logLevel = max(1, conf.logLevel-10)
+
+        if len(opts[1]) > 0:
+            raise getopt.GetoptError("Too many parameters : [%s]" % " ".join(opts[1]))
+
+
+    except getopt.GetoptError as msg:
+        log_loading.error(msg)
+        sys.exit(1)
+
+    if STARTUP_FILE:
+        _read_config_file(STARTUP_FILE)
+    if PRESTART_FILE:
+        _read_config_file(PRESTART_FILE)
+
+    init_session(session_name, mydict)
+
+    if conf.fancy_prompt:
+
+        the_logo = [
+            "                                      ",
+            "                     aSPY//YASa       ",
+            "             apyyyyCY//////////YCa    ",
+            "            sY//////YSpcs  scpCY//Pp  ",
+            " ayp ayyyyyyySCP//Pp           syY//C ",
+            " AYAsAYYYYYYYY///Ps              cY//S",
+            "         pCCCCY//p          cSSps y//Y",
+            "         SPPPP///a          pP///AC//Y",
+            "              A//A            cyP////C",
+            "              p///Ac            sC///a",
+            "              P////YCpc           A//A",
+            "       scccccp///pSP///p          p//Y",
+            "      sY/////////y  caa           S//P",
+            "       cayCyayP//Ya              pY/Ya",
+            "        sY/PsY////YCc          aC//Yp ",
+            "         sc  sccaCY//PCypaapyCP//YSs  ",
+            "                  spCPY//////YPSps    ",
+            "                       ccaacs         ",
+            "                                      ",
+        ]
+
+        the_banner = [
+            "",
+            "",
+            "   |",
+            "   | Welcome to Scapy",
+            "   | Version %s" % conf.version,
+            "   |",
+            "   | https://github.com/secdev/scapy",
+            "   |",
+            "   | Have fun!",
+            "   |",
+        ]
+
+        quote, author = choice(QUOTES)
+        the_banner.extend(_prepare_quote(quote, author, max_len=39))
+        the_banner.append("   |")
+        the_banner = "\n".join(
+            logo + banner for logo, banner in six.moves.zip_longest(
+                (conf.color_theme.logo(line) for line in the_logo),
+                (conf.color_theme.success(line) for line in the_banner),
+                fillvalue=""
+            )
+        )
+    else:
+        the_banner = "Welcome to Scapy (%s)" % conf.version
+    if mybanner is not None:
+        the_banner += "\n"
+        the_banner += mybanner
+
     import atexit
     try:
         import rlcompleter,readline
@@ -355,47 +483,6 @@ def interact(mydict=None,argv=None,mybanner=None,loglevel=20):
         readline.set_completer(ScapyCompleter().complete)
         readline.parse_and_bind("C-o: operate-and-get-next")
         readline.parse_and_bind("tab: complete")
-    
-    
-    STARTUP_FILE = DEFAULT_STARTUP_FILE
-    PRESTART_FILE = DEFAULT_PRESTART_FILE
-
-    session_name = None
-
-    try:
-        opts=getopt.getopt(argv[1:], "hs:Cc:Pp:d")
-        for opt, parm in opts[0]:
-            if opt == "-h":
-                _usage()
-            elif opt == "-s":
-                session_name = parm
-            elif opt == "-c":
-                STARTUP_FILE = parm
-            elif opt == "-C":
-                STARTUP_FILE = None
-            elif opt == "-p":
-                PRESTART_FILE = parm
-            elif opt == "-P":
-                PRESTART_FILE = None
-            elif opt == "-d":
-                conf.logLevel = max(1,conf.logLevel-10)
-        
-        if len(opts[1]) > 0:
-            raise getopt.GetoptError("Too many parameters : [%s]" % " ".join(opts[1]))
-
-
-    except getopt.GetoptError as msg:
-        log_loading.error(msg)
-        sys.exit(1)
-
-    conf.color_theme = DefaultTheme()
-    
-    if STARTUP_FILE:
-        _read_config_file(STARTUP_FILE)
-    if PRESTART_FILE:
-        _read_config_file(PRESTART_FILE)
-
-    init_session(session_name, mydict)
 
     if READLINE:
         if conf.histfile:
@@ -417,7 +504,7 @@ def interact(mydict=None,argv=None,mybanner=None,loglevel=20):
             IPYTHON=False
         
     if IPYTHON:
-        banner = the_banner % (conf.version) + " using IPython %s" % IPython.__version__
+        banner = the_banner + " using IPython %s" % IPython.__version__
 
         # Old way to embed IPython kept for backward compatibility
         try:
@@ -431,8 +518,7 @@ def interact(mydict=None,argv=None,mybanner=None,loglevel=20):
         IPython.embed(user_ns=session, banner2=banner)
 
     else:
-        code.interact(banner = the_banner % (conf.version),
-                      local=session, readfunc=conf.readfunc)
+        code.interact(banner=the_banner, local=session, readfunc=conf.readfunc)
 
     if conf.session:
         save_session(conf.session, session)
diff --git a/scapy/themes.py b/scapy/themes.py
index 46aa87cf..033d0ca1 100644
--- a/scapy/themes.py
+++ b/scapy/themes.py
@@ -85,6 +85,7 @@ class AnsiColorTheme(ColorTheme):
     style_closed = ""
     style_left = ""
     style_right = ""
+    style_logo = ""
 
 class BlackAndWhite(AnsiColorTheme):
     pass
@@ -112,6 +113,7 @@ class DefaultTheme(AnsiColorTheme):
     style_closed = Color.grey
     style_left = Color.blue+Color.invert
     style_right = Color.red+Color.invert
+    style_logo = Color.green+Color.bold
     
 class BrightTheme(AnsiColorTheme):
     style_normal = Color.normal
@@ -131,6 +133,7 @@ class BrightTheme(AnsiColorTheme):
     style_odd = Color.black
     style_left = Color.cyan+Color.invert
     style_right = Color.purple+Color.invert
+    style_logo = Color.green+Color.bold
 
 
 class RastaTheme(AnsiColorTheme):
@@ -153,6 +156,8 @@ class RastaTheme(AnsiColorTheme):
     style_odd = Color.green
     style_left = Color.yellow+Color.invert
     style_right = Color.red+Color.invert
+    style_logo = Color.green+Color.bold
+
 
 class ColorOnBlackTheme(AnsiColorTheme):
     """Color theme for black backgrounds"""
@@ -178,6 +183,7 @@ class ColorOnBlackTheme(AnsiColorTheme):
     style_closed = Color.black+Color.bold
     style_left = Color.cyan+Color.bold
     style_right = Color.red+Color.bold
+    style_logo = Color.green+Color.bold
 
 
 class FormatTheme(ColorTheme):
@@ -204,6 +210,7 @@ class LatexTheme(FormatTheme):
     style_right = r"\textcolor{red}{%s}"
 #    style_even = r"}{\bf "
 #    style_odd = ""
+    style_logo = r"\textcolor{green}{\bf %s}"
 
 class LatexTheme2(FormatTheme):
     style_prompt = r"@`@textcolor@[@blue@]@@[@%s@]@"
@@ -222,6 +229,7 @@ class LatexTheme2(FormatTheme):
 #    style_odd = r"@`@textcolor@[@black@]@@[@@`@bfseries@[@@]@%s@]@"
     style_left = r"@`@textcolor@[@blue@]@@[@%s@]@"
     style_right = r"@`@textcolor@[@red@]@@[@%s@]@"
+    style_logo = r"@`@textcolor@[@green@]@@[@@`@bfseries@[@@]@%s@]@"
 
 class HTMLTheme(FormatTheme):
     style_prompt = "<span class=prompt>%s</span>"
-- 
GitLab