Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions data/txt/sha256sums.txt
Original file line number Diff line number Diff line change
Expand Up @@ -171,13 +171,13 @@ d53a8aecab8af8b8da4dc1c74d868f70a38770d34b1fa50cae4532cae7ce1c87 lib/core/compa
ebe518089733722879f5a13e73020ebe55d46fb7410cacf292ca4ea1d9d1c56a lib/core/convert.py
ae500647c4074681749735a4f3b17b7eca44868dd3f39f9cab0a575888ba04a1 lib/core/data.py
ffae7cfe9f9afb92e887b9a8dbc1630d0063e865f35984ae417b04a4513e5024 lib/core/datatype.py
8a5a6f5313726d6880aeb1ffca35bc2ff6ecd3709b3e987551189a72fed25bf0 lib/core/decorators.py
1d70d75a1c1a2a0ad295f727ee9f1d90cea851dfc2f8c9a85ef79c7975007ead lib/core/decorators.py
d573a37bb00c8b65f75b275aa92549683180fb209b75fd0ff3870e3848939900 lib/core/defaults.py
ce6e1c1766acd95168f7708ddcacaa4a586c21ffc9e92024c4715611c802b60c lib/core/dicts.py
c9d1f64648062d7962caf02c4e2e7d84e8feb2a14451146f627112aae889afcd lib/core/dump.py
c1f211843ccc93a50639ae6f4a50eb434f334e095d9fea440cebe589004374f3 lib/core/enums.py
00a9b29caa81fe4a5ef145202f9c92e6081f90b2a85cd76c878d520d900ad856 lib/core/exception.py
629c0d06d4f4d093badfc8d1de49432d058f66f3223b08dded012eaf05719de2 lib/core/gui.py
1c48804c10b94da696d3470efbd25d2fff0f0bbf2af0101aaac8f8c097fce02b lib/core/gui.py
4608f21a4333c162ab3c266c903fda4793cc5834de30d06affe9b7566dd09811 lib/core/__init__.py
3d308440fb01d04b5d363bfbe0f337756b098532e5bb7a1c91d5213157ec2c35 lib/core/log.py
2a06dc9b5c17a1efdcdb903545729809399f1ee96f7352cc19b9aaa227394ff3 lib/core/optiondict.py
Expand All @@ -188,7 +188,7 @@ c4bfb493a03caf84dd362aec7c248097841de804b7413d0e1ecb8a90c8550bc0 lib/core/readl
d1bd70c1a55858495c727fbec91e30af267459c8f64d50fabf9e4ee2c007e920 lib/core/replication.py
1d0f80b0193ac5204527bfab4bde1a7aee0f693fd008e86b4b29f606d1ef94f3 lib/core/revision.py
d2eb8e4b05ac93551272b3d4abfaf5b9f2d3ac92499a7704c16ed0b4f200db38 lib/core/session.py
671255b7fd3714ef4315b8ff6f73e09496170d21796dd5e73062654be4ed615e lib/core/settings.py
84eb7f5d155afce459120ab42efb8f5baa104f8ed2f8e64d8d8cbdea62a79dc7 lib/core/settings.py
1c5eab9494eb969bc9ce118a2ea6954690c6851cbe54c18373c723b99734bf09 lib/core/shell.py
4eea6dcf023e41e3c64b210cb5c2efc7ca893b727f5e49d9c924f076bb224053 lib/core/subprocessng.py
cdd352e1331c6b535e780f6edea79465cb55af53aa2114dcea0e8bf382e56d1a lib/core/target.py
Expand Down
6 changes: 3 additions & 3 deletions lib/core/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ def cachedmethod(f):
def _f(*args, **kwargs):
parts = (
f.__module__ + "." + f.__name__,
"|".join(repr(a) for a in args),
"|".join("%s=%r" % (k, kwargs[k]) for k in sorted(kwargs))
"^".join(repr(a) for a in args),
"^".join("%s=%r" % (k, kwargs[k]) for k in sorted(kwargs))
)
try:
key = int(hashlib.md5("|".join(parts).encode(UNICODE_ENCODING)).hexdigest(), 16) & 0x7fffffffffffffff
key = int(hashlib.md5("`".join(parts).encode(UNICODE_ENCODING)).hexdigest(), 16) & 0x7fffffffffffffff
except ValueError: # https://github.com/sqlmapproject/sqlmap/issues/4281 (NOTE: non-standard Python behavior where hexdigest returns binary value)
result = f(*args, **kwargs)
else:
Expand Down
248 changes: 195 additions & 53 deletions lib/core/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,6 @@ def check(self, *args):
else:
self.set(self.old_value)

# Reference: https://code.activestate.com/recipes/580726-tkinter-notebook-that-fits-to-the-height-of-every-/
class AutoresizableNotebook(_tkinter_ttk.Notebook):
def __init__(self, master=None, **kw):
_tkinter_ttk.Notebook.__init__(self, master, **kw)
self.bind("<<NotebookTabChanged>>", self._on_tab_changed)

def _on_tab_changed(self, event):
event.widget.update_idletasks()

tab = event.widget.nametowidget(event.widget.select())
event.widget.configure(height=tab.winfo_reqheight())

try:
window = _tkinter.Tk()
except Exception as ex:
Expand All @@ -81,11 +69,41 @@ def _on_tab_changed(self, event):

window.title(VERSION_STRING)

# Reference: https://www.holadevs.com/pregunta/64750/change-selected-tab-color-in-ttknotebook
# Set theme and colors
bg_color = "#f5f5f5"
fg_color = "#333333"
accent_color = "#2c7fb8"
window.configure(background=bg_color)

# Configure styles
style = _tkinter_ttk.Style()
settings = {"TNotebook.Tab": {"configure": {"padding": [5, 1], "background": "#fdd57e"}, "map": {"background": [("selected", "#C70039"), ("active", "#fc9292")], "foreground": [("selected", "#ffffff"), ("active", "#000000")]}}}
style.theme_create("custom", parent="alt", settings=settings)
style.theme_use("custom")

# Try to use a more modern theme if available
available_themes = style.theme_names()
if 'clam' in available_themes:
style.theme_use('clam')
elif 'alt' in available_themes:
style.theme_use('alt')

# Configure notebook style
style.configure("TNotebook", background=bg_color)
style.configure("TNotebook.Tab",
padding=[10, 4],
background="#e1e1e1",
font=('Helvetica', 9))
style.map("TNotebook.Tab",
background=[("selected", accent_color), ("active", "#7fcdbb")],
foreground=[("selected", "white"), ("active", "white")])

# Configure button style
style.configure("TButton",
padding=4,
relief="flat",
background=accent_color,
foreground="white",
font=('Helvetica', 9))
style.map("TButton",
background=[('active', '#41b6c4')])

# Reference: https://stackoverflow.com/a/10018670
def center(window):
Expand Down Expand Up @@ -138,24 +156,26 @@ def run():
config = {}

for key in window._widgets:
dest, type = key
dest, widget_type = key
widget = window._widgets[key]

if hasattr(widget, "get") and not widget.get():
value = None
elif type == "string":
elif widget_type == "string":
value = widget.get()
elif type == "float":
elif widget_type == "float":
value = float(widget.get())
elif type == "int":
elif widget_type == "int":
value = int(widget.get())
else:
value = bool(widget.var.get())

config[dest] = value

for option in parser.option_list:
config[option.dest] = defaults.get(option.dest, None)
# Only set default if not already set by the user
if option.dest not in config or config[option.dest] is None:
config[option.dest] = defaults.get(option.dest, None)

handle, configFile = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.CONFIG, text=True)
os.close(handle)
Expand Down Expand Up @@ -183,20 +203,27 @@ def enqueue(stream, queue):

top = _tkinter.Toplevel()
top.title("Console")
top.configure(background=bg_color)

# Create a frame for the console
console_frame = _tkinter.Frame(top, bg=bg_color)
console_frame.pack(fill=_tkinter.BOTH, expand=True, padx=10, pady=10)

# Reference: https://stackoverflow.com/a/13833338
text = _tkinter_scrolledtext.ScrolledText(top, undo=True)
text = _tkinter_scrolledtext.ScrolledText(console_frame, undo=True, wrap=_tkinter.WORD,
bg="#2c3e50", fg="#ecf0f1",
insertbackground="white",
font=('Consolas', 10))
text.bind("<Key>", onKeyPress)
text.bind("<Return>", onReturnPress)
text.pack()
text.pack(fill=_tkinter.BOTH, expand=True)
text.focus()

center(top)

while True:
line = ""
try:
# line = queue.get_nowait()
line = queue.get(timeout=.1)
text.insert(_tkinter.END, line)
except _queue.Empty:
Expand All @@ -206,9 +233,10 @@ def enqueue(stream, queue):
if not alive:
break

menubar = _tkinter.Menu(window)
# Create a menu bar
menubar = _tkinter.Menu(window, bg=bg_color, fg=fg_color)

filemenu = _tkinter.Menu(menubar, tearoff=0)
filemenu = _tkinter.Menu(menubar, tearoff=0, bg=bg_color, fg=fg_color)
filemenu.add_command(label="Open", state=_tkinter.DISABLED)
filemenu.add_command(label="Save", state=_tkinter.DISABLED)
filemenu.add_separator()
Expand All @@ -217,7 +245,7 @@ def enqueue(stream, queue):

menubar.add_command(label="Run", command=run)

helpmenu = _tkinter.Menu(menubar, tearoff=0)
helpmenu = _tkinter.Menu(menubar, tearoff=0, bg=bg_color, fg=fg_color)
helpmenu.add_command(label="Official site", command=lambda: webbrowser.open(SITE))
helpmenu.add_command(label="Github pages", command=lambda: webbrowser.open(GIT_PAGE))
helpmenu.add_command(label="Wiki pages", command=lambda: webbrowser.open(WIKI_PAGE))
Expand All @@ -226,59 +254,173 @@ def enqueue(stream, queue):
helpmenu.add_command(label="About", command=lambda: _tkinter_messagebox.showinfo("About", "Copyright (c) 2006-2025\n\n (%s)" % DEV_EMAIL_ADDRESS))
menubar.add_cascade(label="Help", menu=helpmenu)

window.config(menu=menubar)
window.config(menu=menubar, bg=bg_color)
window._widgets = {}

notebook = AutoresizableNotebook(window)
# Create header frame
header_frame = _tkinter.Frame(window, bg=bg_color, height=60)
header_frame.pack(fill=_tkinter.X, pady=(0, 5))
header_frame.pack_propagate(0)

first = None
frames = {}
# Add header label
title_label = _tkinter.Label(header_frame, text="Configuration",
font=('Helvetica', 14),
fg=accent_color, bg=bg_color)
title_label.pack(side=_tkinter.LEFT, padx=15)

for group in parser.option_groups:
frame = frames[group.title] = _tkinter.Frame(notebook, width=200, height=200)
notebook.add(frames[group.title], text=group.title)
# Add run button in header
run_button = _tkinter_ttk.Button(header_frame, text="Run", command=run, width=12)
run_button.pack(side=_tkinter.RIGHT, padx=15)

# Create notebook
notebook = _tkinter_ttk.Notebook(window)
notebook.pack(expand=1, fill="both", padx=5, pady=(0, 5))

_tkinter.Label(frame).grid(column=0, row=0, sticky=_tkinter.W)
# Store tab information for background loading
tab_frames = {}
tab_canvases = {}
tab_scrollable_frames = {}
tab_groups = {}

# Create empty tabs with scrollable areas first (fast)
for group in parser.option_groups:
# Create a frame with scrollbar for the tab
tab_frame = _tkinter.Frame(notebook, bg=bg_color)
tab_frames[group.title] = tab_frame

# Create a canvas with scrollbar
canvas = _tkinter.Canvas(tab_frame, bg=bg_color, highlightthickness=0)
scrollbar = _tkinter_ttk.Scrollbar(tab_frame, orient="vertical", command=canvas.yview)
scrollable_frame = _tkinter.Frame(canvas, bg=bg_color)

# Store references
tab_canvases[group.title] = canvas
tab_scrollable_frames[group.title] = scrollable_frame
tab_groups[group.title] = group

# Configure the canvas scrolling
scrollable_frame.bind(
"<Configure>",
lambda e, canvas=canvas: canvas.configure(scrollregion=canvas.bbox("all"))
)

canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
canvas.configure(yscrollcommand=scrollbar.set)

# Pack the canvas and scrollbar
canvas.pack(side="left", fill="both", expand=True)
scrollbar.pack(side="right", fill="y")

# Add the tab to the notebook
notebook.add(tab_frame, text=group.title)

# Add a loading indicator
loading_label = _tkinter.Label(scrollable_frame, text="Loading options...",
font=('Helvetica', 12),
fg=accent_color, bg=bg_color)
loading_label.pack(expand=True)

# Function to populate a tab in the background
def populate_tab(tab_name):
group = tab_groups[tab_name]
scrollable_frame = tab_scrollable_frames[tab_name]
canvas = tab_canvases[tab_name]

# Remove loading indicator
for child in scrollable_frame.winfo_children():
child.destroy()

# Add content to the scrollable frame
row = 0

row = 1
if group.get_description():
_tkinter.Label(frame, text="%s:" % group.get_description()).grid(column=0, row=1, columnspan=3, sticky=_tkinter.W)
_tkinter.Label(frame).grid(column=0, row=2, sticky=_tkinter.W)
row += 2
desc_label = _tkinter.Label(scrollable_frame, text=group.get_description(),
wraplength=600, justify="left",
font=('Helvetica', 9),
fg="#555555", bg=bg_color)
desc_label.grid(row=row, column=0, columnspan=3, sticky="w", padx=10, pady=(10, 5))
row += 1

for option in group.option_list:
_tkinter.Label(frame, text="%s " % parser.formatter._format_option_strings(option)).grid(column=0, row=row, sticky=_tkinter.W)

# Option label
option_label = _tkinter.Label(scrollable_frame,
text=parser.formatter._format_option_strings(option) + ":",
font=('Helvetica', 9),
fg=fg_color, bg=bg_color,
anchor="w")
option_label.grid(row=row, column=0, sticky="w", padx=10, pady=2)

# Input widget
if option.type == "string":
widget = _tkinter.Entry(frame)
widget = _tkinter.Entry(scrollable_frame, font=('Helvetica', 9),
relief="sunken", bd=1, width=20)
widget.grid(row=row, column=1, sticky="w", padx=5, pady=2)
elif option.type == "float":
widget = ConstrainedEntry(frame, regex=r"\A\d*\.?\d*\Z")
widget = ConstrainedEntry(scrollable_frame, regex=r"\A\d*\.?\d*\Z",
font=('Helvetica', 9),
relief="sunken", bd=1, width=10)
widget.grid(row=row, column=1, sticky="w", padx=5, pady=2)
elif option.type == "int":
widget = ConstrainedEntry(frame, regex=r"\A\d*\Z")
widget = ConstrainedEntry(scrollable_frame, regex=r"\A\d*\Z",
font=('Helvetica', 9),
relief="sunken", bd=1, width=10)
widget.grid(row=row, column=1, sticky="w", padx=5, pady=2)
else:
var = _tkinter.IntVar()
widget = _tkinter.Checkbutton(frame, variable=var)
widget = _tkinter.Checkbutton(scrollable_frame, variable=var,
bg=bg_color, activebackground=bg_color)
widget.var = var
widget.grid(row=row, column=1, sticky="w", padx=5, pady=2)

# Help text (truncated to improve performance)
help_text = option.help
if len(help_text) > 100:
help_text = help_text[:100] + "..."

first = first or widget
widget.grid(column=1, row=row, sticky=_tkinter.W)
help_label = _tkinter.Label(scrollable_frame, text=help_text,
font=('Helvetica', 8),
fg="#666666", bg=bg_color,
wraplength=400, justify="left")
help_label.grid(row=row, column=2, sticky="w", padx=5, pady=2)

# Store widget reference
window._widgets[(option.dest, option.type)] = widget

# Set default value
default = defaults.get(option.dest)
if default:
if hasattr(widget, "insert"):
widget.insert(0, default)

_tkinter.Label(frame, text=" %s" % option.help).grid(column=2, row=row, sticky=_tkinter.W)
elif hasattr(widget, "var"):
widget.var.set(1 if default else 0)

row += 1

_tkinter.Label(frame).grid(column=0, row=row, sticky=_tkinter.W)
# Add some padding at the bottom
_tkinter.Label(scrollable_frame, bg=bg_color, height=1).grid(row=row, column=0)

# Update the scroll region after adding all widgets
canvas.update_idletasks()
canvas.configure(scrollregion=canvas.bbox("all"))

# Update the UI to show the tab is fully loaded
window.update_idletasks()

# Function to populate tabs in the background
def populate_tabs_background():
for tab_name in tab_groups.keys():
# Schedule each tab to be populated with a small delay between them
window.after(100, lambda name=tab_name: populate_tab(name))

# Start populating tabs in the background after a short delay
window.after(500, populate_tabs_background)

notebook.pack(expand=1, fill="both")
notebook.enable_traversal()
# Set minimum window size
window.update()
window.minsize(800, 500)

first.focus()
# Center the window on screen
center(window)

# Start the GUI
window.mainloop()
2 changes: 1 addition & 1 deletion lib/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from thirdparty import six

# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
VERSION = "1.9.9.2"
VERSION = "1.9.9.4"
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34}
VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE)
Expand Down