Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sqlmapproject
GitHub Repository: sqlmapproject/sqlmap
Path: blob/master/lib/utils/gui.py
3556 views
1
#!/usr/bin/env python
2
3
"""
4
Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org)
5
See the file 'LICENSE' for copying permission
6
"""
7
8
import os
9
import re
10
import socket
11
import subprocess
12
import sys
13
import tempfile
14
import threading
15
import webbrowser
16
17
from lib.core.common import getSafeExString
18
from lib.core.common import saveConfig
19
from lib.core.data import paths
20
from lib.core.defaults import defaults
21
from lib.core.enums import MKSTEMP_PREFIX
22
from lib.core.exception import SqlmapMissingDependence
23
from lib.core.exception import SqlmapSystemException
24
from lib.core.settings import DEV_EMAIL_ADDRESS
25
from lib.core.settings import IS_WIN
26
from lib.core.settings import ISSUES_PAGE
27
from lib.core.settings import GIT_PAGE
28
from lib.core.settings import SITE
29
from lib.core.settings import VERSION_STRING
30
from lib.core.settings import WIKI_PAGE
31
from thirdparty.six.moves import queue as _queue
32
33
alive = None
34
line = ""
35
process = None
36
queue = None
37
38
def runGui(parser):
39
try:
40
from thirdparty.six.moves import tkinter as _tkinter
41
from thirdparty.six.moves import tkinter_scrolledtext as _tkinter_scrolledtext
42
from thirdparty.six.moves import tkinter_ttk as _tkinter_ttk
43
from thirdparty.six.moves import tkinter_messagebox as _tkinter_messagebox
44
except ImportError as ex:
45
raise SqlmapMissingDependence("missing dependence ('%s')" % getSafeExString(ex))
46
47
# Reference: https://www.reddit.com/r/learnpython/comments/985umy/limit_user_input_to_only_int_with_tkinter/e4dj9k9?utm_source=share&utm_medium=web2x
48
class ConstrainedEntry(_tkinter.Entry):
49
def __init__(self, master=None, **kwargs):
50
self.var = _tkinter.StringVar()
51
self.regex = kwargs["regex"]
52
del kwargs["regex"]
53
_tkinter.Entry.__init__(self, master, textvariable=self.var, **kwargs)
54
self.old_value = ''
55
self.var.trace('w', self.check)
56
self.get, self.set = self.var.get, self.var.set
57
58
def check(self, *args):
59
if re.search(self.regex, self.get()):
60
self.old_value = self.get()
61
else:
62
self.set(self.old_value)
63
64
try:
65
window = _tkinter.Tk()
66
except Exception as ex:
67
errMsg = "unable to create GUI window ('%s')" % getSafeExString(ex)
68
raise SqlmapSystemException(errMsg)
69
70
window.title("sqlmap - Tkinter GUI")
71
72
# Set theme and colors
73
bg_color = "#f5f5f5"
74
fg_color = "#333333"
75
accent_color = "#2c7fb8"
76
window.configure(background=bg_color)
77
78
# Configure styles
79
style = _tkinter_ttk.Style()
80
81
# Try to use a more modern theme if available
82
available_themes = style.theme_names()
83
if 'clam' in available_themes:
84
style.theme_use('clam')
85
elif 'alt' in available_themes:
86
style.theme_use('alt')
87
88
# Configure notebook style
89
style.configure("TNotebook", background=bg_color)
90
style.configure("TNotebook.Tab",
91
padding=[10, 4],
92
background="#e1e1e1",
93
font=('Helvetica', 9))
94
style.map("TNotebook.Tab",
95
background=[("selected", accent_color), ("active", "#7fcdbb")],
96
foreground=[("selected", "white"), ("active", "white")])
97
98
# Configure button style
99
style.configure("TButton",
100
padding=4,
101
relief="flat",
102
background=accent_color,
103
foreground="white",
104
font=('Helvetica', 9))
105
style.map("TButton",
106
background=[('active', '#41b6c4')])
107
108
# Reference: https://stackoverflow.com/a/10018670
109
def center(window):
110
window.update_idletasks()
111
width = window.winfo_width()
112
frm_width = window.winfo_rootx() - window.winfo_x()
113
win_width = width + 2 * frm_width
114
height = window.winfo_height()
115
titlebar_height = window.winfo_rooty() - window.winfo_y()
116
win_height = height + titlebar_height + frm_width
117
x = window.winfo_screenwidth() // 2 - win_width // 2
118
y = window.winfo_screenheight() // 2 - win_height // 2
119
window.geometry('{}x{}+{}+{}'.format(width, height, x, y))
120
window.deiconify()
121
122
def onKeyPress(event):
123
global line
124
global queue
125
126
if process:
127
if event.char == '\b':
128
line = line[:-1]
129
else:
130
line += event.char
131
132
def onReturnPress(event):
133
global line
134
global queue
135
136
if process:
137
try:
138
process.stdin.write(("%s\n" % line.strip()).encode())
139
process.stdin.flush()
140
except socket.error:
141
line = ""
142
event.widget.master.master.destroy()
143
return "break"
144
except:
145
return
146
147
event.widget.insert(_tkinter.END, "\n")
148
149
return "break"
150
151
def run():
152
global alive
153
global process
154
global queue
155
156
config = {}
157
158
for key in window._widgets:
159
dest, widget_type = key
160
widget = window._widgets[key]
161
162
if hasattr(widget, "get") and not widget.get():
163
value = None
164
elif widget_type == "string":
165
value = widget.get()
166
elif widget_type == "float":
167
value = float(widget.get())
168
elif widget_type == "int":
169
value = int(widget.get())
170
else:
171
value = bool(widget.var.get())
172
173
config[dest] = value
174
175
for option in parser.option_list:
176
# Only set default if not already set by the user
177
if option.dest not in config or config[option.dest] is None:
178
config[option.dest] = defaults.get(option.dest, None)
179
180
handle, configFile = tempfile.mkstemp(prefix=MKSTEMP_PREFIX.CONFIG, text=True)
181
os.close(handle)
182
183
saveConfig(config, configFile)
184
185
def enqueue(stream, queue):
186
global alive
187
188
for line in iter(stream.readline, b''):
189
queue.put(line)
190
191
alive = False
192
stream.close()
193
194
alive = True
195
196
process = subprocess.Popen([sys.executable or "python", os.path.join(paths.SQLMAP_ROOT_PATH, "sqlmap.py"), "-c", configFile], shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.PIPE, bufsize=1, close_fds=not IS_WIN)
197
198
# Reference: https://stackoverflow.com/a/4896288
199
queue = _queue.Queue()
200
thread = threading.Thread(target=enqueue, args=(process.stdout, queue))
201
thread.daemon = True
202
thread.start()
203
204
top = _tkinter.Toplevel()
205
top.title("Console")
206
top.configure(background=bg_color)
207
208
# Create a frame for the console
209
console_frame = _tkinter.Frame(top, bg=bg_color)
210
console_frame.pack(fill=_tkinter.BOTH, expand=True, padx=10, pady=10)
211
212
# Reference: https://stackoverflow.com/a/13833338
213
text = _tkinter_scrolledtext.ScrolledText(console_frame, undo=True, wrap=_tkinter.WORD,
214
bg="#2c3e50", fg="#ecf0f1",
215
insertbackground="white",
216
font=('Consolas', 10))
217
text.bind("<Key>", onKeyPress)
218
text.bind("<Return>", onReturnPress)
219
text.pack(fill=_tkinter.BOTH, expand=True)
220
text.focus()
221
222
center(top)
223
224
while True:
225
line = ""
226
try:
227
line = queue.get(timeout=.1)
228
text.insert(_tkinter.END, line)
229
except _queue.Empty:
230
text.see(_tkinter.END)
231
text.update_idletasks()
232
233
if not alive:
234
break
235
236
# Create a menu bar
237
menubar = _tkinter.Menu(window, bg=bg_color, fg=fg_color)
238
239
filemenu = _tkinter.Menu(menubar, tearoff=0, bg=bg_color, fg=fg_color)
240
filemenu.add_command(label="Open", state=_tkinter.DISABLED)
241
filemenu.add_command(label="Save", state=_tkinter.DISABLED)
242
filemenu.add_separator()
243
filemenu.add_command(label="Exit", command=window.quit)
244
menubar.add_cascade(label="File", menu=filemenu)
245
246
menubar.add_command(label="Run", command=run)
247
248
helpmenu = _tkinter.Menu(menubar, tearoff=0, bg=bg_color, fg=fg_color)
249
helpmenu.add_command(label="Official site", command=lambda: webbrowser.open(SITE))
250
helpmenu.add_command(label="Github pages", command=lambda: webbrowser.open(GIT_PAGE))
251
helpmenu.add_command(label="Wiki pages", command=lambda: webbrowser.open(WIKI_PAGE))
252
helpmenu.add_command(label="Report issue", command=lambda: webbrowser.open(ISSUES_PAGE))
253
helpmenu.add_separator()
254
helpmenu.add_command(label="About", command=lambda: _tkinter_messagebox.showinfo("About", "%s\n\n (%s)" % (VERSION_STRING, DEV_EMAIL_ADDRESS)))
255
menubar.add_cascade(label="Help", menu=helpmenu)
256
257
window.config(menu=menubar, bg=bg_color)
258
window._widgets = {}
259
260
# Create header frame
261
header_frame = _tkinter.Frame(window, bg=bg_color, height=60)
262
header_frame.pack(fill=_tkinter.X, pady=(0, 5))
263
header_frame.pack_propagate(0)
264
265
# Add header label
266
title_label = _tkinter.Label(header_frame, text="Configuration",
267
font=('Helvetica', 14),
268
fg=accent_color, bg=bg_color)
269
title_label.pack(side=_tkinter.LEFT, padx=15)
270
271
# Add run button in header
272
run_button = _tkinter_ttk.Button(header_frame, text="Run", command=run, width=12)
273
run_button.pack(side=_tkinter.RIGHT, padx=15)
274
275
# Create notebook
276
notebook = _tkinter_ttk.Notebook(window)
277
notebook.pack(expand=1, fill="both", padx=5, pady=(0, 5))
278
279
# Store tab information for background loading
280
tab_frames = {}
281
tab_canvases = {}
282
tab_scrollable_frames = {}
283
tab_groups = {}
284
285
# Create empty tabs with scrollable areas first (fast)
286
for group in parser.option_groups:
287
# Create a frame with scrollbar for the tab
288
tab_frame = _tkinter.Frame(notebook, bg=bg_color)
289
tab_frames[group.title] = tab_frame
290
291
# Create a canvas with scrollbar
292
canvas = _tkinter.Canvas(tab_frame, bg=bg_color, highlightthickness=0)
293
scrollbar = _tkinter_ttk.Scrollbar(tab_frame, orient="vertical", command=canvas.yview)
294
scrollable_frame = _tkinter.Frame(canvas, bg=bg_color)
295
296
# Store references
297
tab_canvases[group.title] = canvas
298
tab_scrollable_frames[group.title] = scrollable_frame
299
tab_groups[group.title] = group
300
301
# Configure the canvas scrolling
302
scrollable_frame.bind(
303
"<Configure>",
304
lambda e, canvas=canvas: canvas.configure(scrollregion=canvas.bbox("all"))
305
)
306
307
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
308
canvas.configure(yscrollcommand=scrollbar.set)
309
310
# Pack the canvas and scrollbar
311
canvas.pack(side="left", fill="both", expand=True)
312
scrollbar.pack(side="right", fill="y")
313
314
# Add the tab to the notebook
315
notebook.add(tab_frame, text=group.title)
316
317
# Add a loading indicator
318
loading_label = _tkinter.Label(scrollable_frame, text="Loading options...",
319
font=('Helvetica', 12),
320
fg=accent_color, bg=bg_color)
321
loading_label.pack(expand=True)
322
323
# Function to populate a tab in the background
324
def populate_tab(tab_name):
325
group = tab_groups[tab_name]
326
scrollable_frame = tab_scrollable_frames[tab_name]
327
canvas = tab_canvases[tab_name]
328
329
# Remove loading indicator
330
for child in scrollable_frame.winfo_children():
331
child.destroy()
332
333
# Add content to the scrollable frame
334
row = 0
335
336
if group.get_description():
337
desc_label = _tkinter.Label(scrollable_frame, text=group.get_description(),
338
wraplength=600, justify="left",
339
font=('Helvetica', 9),
340
fg="#555555", bg=bg_color)
341
desc_label.grid(row=row, column=0, columnspan=3, sticky="w", padx=10, pady=(10, 5))
342
row += 1
343
344
for option in group.option_list:
345
# Option label
346
option_label = _tkinter.Label(scrollable_frame,
347
text=parser.formatter._format_option_strings(option) + ":",
348
font=('Helvetica', 9),
349
fg=fg_color, bg=bg_color,
350
anchor="w")
351
option_label.grid(row=row, column=0, sticky="w", padx=10, pady=2)
352
353
# Input widget
354
if option.type == "string":
355
widget = _tkinter.Entry(scrollable_frame, font=('Helvetica', 9),
356
relief="sunken", bd=1, width=20)
357
widget.grid(row=row, column=1, sticky="w", padx=5, pady=2)
358
elif option.type == "float":
359
widget = ConstrainedEntry(scrollable_frame, regex=r"\A\d*\.?\d*\Z",
360
font=('Helvetica', 9),
361
relief="sunken", bd=1, width=10)
362
widget.grid(row=row, column=1, sticky="w", padx=5, pady=2)
363
elif option.type == "int":
364
widget = ConstrainedEntry(scrollable_frame, regex=r"\A\d*\Z",
365
font=('Helvetica', 9),
366
relief="sunken", bd=1, width=10)
367
widget.grid(row=row, column=1, sticky="w", padx=5, pady=2)
368
else:
369
var = _tkinter.IntVar()
370
widget = _tkinter.Checkbutton(scrollable_frame, variable=var,
371
bg=bg_color, activebackground=bg_color)
372
widget.var = var
373
widget.grid(row=row, column=1, sticky="w", padx=5, pady=2)
374
375
# Help text (truncated to improve performance)
376
help_text = option.help
377
if len(help_text) > 100:
378
help_text = help_text[:100] + "..."
379
380
help_label = _tkinter.Label(scrollable_frame, text=help_text,
381
font=('Helvetica', 8),
382
fg="#666666", bg=bg_color,
383
wraplength=400, justify="left")
384
help_label.grid(row=row, column=2, sticky="w", padx=5, pady=2)
385
386
# Store widget reference
387
window._widgets[(option.dest, option.type)] = widget
388
389
# Set default value
390
default = defaults.get(option.dest)
391
if default:
392
if hasattr(widget, "insert"):
393
widget.insert(0, default)
394
elif hasattr(widget, "var"):
395
widget.var.set(1 if default else 0)
396
397
row += 1
398
399
# Add some padding at the bottom
400
_tkinter.Label(scrollable_frame, bg=bg_color, height=1).grid(row=row, column=0)
401
402
# Update the scroll region after adding all widgets
403
canvas.update_idletasks()
404
canvas.configure(scrollregion=canvas.bbox("all"))
405
406
# Update the UI to show the tab is fully loaded
407
window.update_idletasks()
408
409
# Function to populate tabs in the background
410
def populate_tabs_background():
411
for tab_name in tab_groups.keys():
412
# Schedule each tab to be populated with a small delay between them
413
window.after(100, lambda name=tab_name: populate_tab(name))
414
415
# Start populating tabs in the background after a short delay
416
window.after(500, populate_tabs_background)
417
418
# Set minimum window size
419
window.update()
420
window.minsize(800, 500)
421
422
# Center the window on screen
423
center(window)
424
425
# Start the GUI
426
window.mainloop()
427
428