396 lines
5.5 KiB
Python
396 lines
5.5 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
Server-side Trips report page for .wifitrip import/export.
|
|
"""
|
|
|
|
from html import escape
|
|
|
|
from reports.template import (
|
|
ReportContext,
|
|
page_begin,
|
|
page_end,
|
|
)
|
|
|
|
|
|
def _stat_card(label, value):
|
|
return f"""
|
|
<div class="stat-card">
|
|
|
|
<div class="stat-label">
|
|
{escape(str(label))}
|
|
</div>
|
|
|
|
<div class="stat-value">
|
|
{escape(str(value))}
|
|
</div>
|
|
|
|
</div>
|
|
"""
|
|
|
|
|
|
def _message(message):
|
|
if not message:
|
|
return ""
|
|
|
|
message_type = escape(
|
|
str(message.get("type", "info"))
|
|
)
|
|
|
|
message_text = escape(
|
|
str(message.get("text", ""))
|
|
)
|
|
|
|
return f"""
|
|
<section class="card">
|
|
|
|
<div class="info {message_type}">
|
|
{message_text}
|
|
</div>
|
|
|
|
</section>
|
|
"""
|
|
|
|
|
|
def _import_result(result):
|
|
if not result:
|
|
return ""
|
|
|
|
html = """
|
|
<section class="card">
|
|
|
|
<h2>Import result</h2>
|
|
|
|
<div class="stats-grid">
|
|
"""
|
|
|
|
html += _stat_card(
|
|
"Sessions in file",
|
|
result.get("sessions", 0),
|
|
)
|
|
|
|
html += _stat_card(
|
|
"Imported",
|
|
result.get("imported", 0),
|
|
)
|
|
|
|
html += _stat_card(
|
|
"Duplicates",
|
|
result.get("duplicates", 0),
|
|
)
|
|
|
|
html += _stat_card(
|
|
"Failed",
|
|
result.get("failed", 0),
|
|
)
|
|
|
|
html += """
|
|
</div>
|
|
"""
|
|
|
|
failed_sessions = result.get(
|
|
"failed_sessions",
|
|
[],
|
|
)
|
|
|
|
if failed_sessions:
|
|
|
|
html += """
|
|
<h3>Failed sessions</h3>
|
|
|
|
<div class="table-wrapper">
|
|
|
|
<table>
|
|
|
|
<thead>
|
|
<tr>
|
|
<th>Session hash</th>
|
|
<th>Error</th>
|
|
</tr>
|
|
</thead>
|
|
|
|
<tbody>
|
|
"""
|
|
|
|
for item in failed_sessions:
|
|
|
|
html += f"""
|
|
<tr>
|
|
|
|
<td>
|
|
<code>
|
|
{escape(str(item.get("session_hash", "")))}
|
|
</code>
|
|
</td>
|
|
|
|
<td>
|
|
{escape(str(item.get("error", "")))}
|
|
</td>
|
|
|
|
</tr>
|
|
"""
|
|
|
|
html += """
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
</div>
|
|
"""
|
|
|
|
html += """
|
|
</section>
|
|
"""
|
|
|
|
return html
|
|
|
|
|
|
def _validation_result(validation):
|
|
if not validation:
|
|
return ""
|
|
|
|
if not validation.get("valid"):
|
|
|
|
return f"""
|
|
<section class="card">
|
|
|
|
<h2>Package validation</h2>
|
|
|
|
<div class="info error">
|
|
{escape(
|
|
str(
|
|
validation.get(
|
|
"error",
|
|
"Package is invalid."
|
|
)
|
|
)
|
|
)}
|
|
</div>
|
|
|
|
</section>
|
|
"""
|
|
|
|
return f"""
|
|
<section class="card">
|
|
|
|
<h2>Package validation</h2>
|
|
|
|
<div class="info success">
|
|
Package is valid and contains
|
|
{escape(str(validation.get("sessions", 0)))}
|
|
session(s).
|
|
</div>
|
|
|
|
<div class="stats-grid">
|
|
|
|
{_stat_card(
|
|
"Sessions",
|
|
validation.get("sessions", 0)
|
|
)}
|
|
|
|
{_stat_card(
|
|
"Access points",
|
|
validation.get("access_points", 0)
|
|
)}
|
|
|
|
{_stat_card(
|
|
"GPS points",
|
|
validation.get("gps_points", 0)
|
|
)}
|
|
|
|
{_stat_card(
|
|
"Observations",
|
|
validation.get("observations", 0)
|
|
)}
|
|
|
|
{_stat_card(
|
|
"Handshakes",
|
|
validation.get("handshakes", 0)
|
|
)}
|
|
|
|
{_stat_card(
|
|
"Handshake Cracked",
|
|
validation.get("credentials", 0)
|
|
)}
|
|
|
|
</div>
|
|
|
|
</section>
|
|
"""
|
|
|
|
|
|
def render_trips_page(
|
|
conn,
|
|
context=None,
|
|
message=None,
|
|
import_result=None,
|
|
validation=None,
|
|
):
|
|
if context is None:
|
|
|
|
context = ReportContext(
|
|
mode="server",
|
|
page="trips",
|
|
)
|
|
|
|
session_count = conn.execute(
|
|
"""
|
|
SELECT COUNT(*) AS count
|
|
FROM capture_sessions
|
|
"""
|
|
).fetchone()["count"]
|
|
|
|
html = page_begin(
|
|
"Trips",
|
|
(
|
|
"Export and import capture sessions "
|
|
"as .wifitrip packages."
|
|
),
|
|
"trips",
|
|
context,
|
|
)
|
|
|
|
html += _message(
|
|
message
|
|
)
|
|
|
|
html += """
|
|
<section class="card">
|
|
|
|
<h2>Export capture sessions</h2>
|
|
|
|
<p>
|
|
Export all capture sessions into one
|
|
<code>.wifitrip</code> package.
|
|
</p>
|
|
|
|
<p>
|
|
The package contains logical session data:
|
|
GPS track, access-point observations,
|
|
handshakes and credentials. Physical capture
|
|
files are not included.
|
|
</p>
|
|
|
|
<div class="stats-grid">
|
|
"""
|
|
|
|
html += _stat_card(
|
|
"Sessions available",
|
|
session_count,
|
|
)
|
|
|
|
html += """
|
|
</div>
|
|
|
|
<form
|
|
method="get"
|
|
action="/reports/trips/export"
|
|
>
|
|
|
|
<button type="submit" class="button">
|
|
Export all sessions
|
|
</button>
|
|
|
|
</form>
|
|
|
|
</section>
|
|
"""
|
|
|
|
html += """
|
|
<section class="card">
|
|
|
|
<h2>Import capture sessions</h2>
|
|
|
|
<p>
|
|
Import one <code>.wifitrip</code> package.
|
|
The complete package is validated before
|
|
any database changes are made.
|
|
</p>
|
|
|
|
<form
|
|
method="post"
|
|
action="/reports/trips/import"
|
|
enctype="multipart/form-data"
|
|
>
|
|
|
|
<label for="wifitrip_file">
|
|
.wifitrip file
|
|
</label>
|
|
|
|
<div class="wifitrip-file-row">
|
|
|
|
<input
|
|
type="file"
|
|
id="wifitrip_file"
|
|
name="wifitrip_file"
|
|
accept=".wifitrip,application/zip"
|
|
required
|
|
>
|
|
|
|
<label
|
|
for="wifitrip_file"
|
|
class="button"
|
|
>
|
|
Browse
|
|
</label>
|
|
|
|
<span
|
|
class="wifitrip-file-name"
|
|
id="wifitrip-file-name"
|
|
>
|
|
Файл не выбран
|
|
</span>
|
|
|
|
</div>
|
|
|
|
<div style="margin-top: 1rem;">
|
|
|
|
<button type="submit" class="button">
|
|
Import package
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</form>
|
|
|
|
<script>
|
|
const wifitripFileInput = document.getElementById(
|
|
"wifitrip_file"
|
|
);
|
|
|
|
const wifitripFileName = document.getElementById(
|
|
"wifitrip-file-name"
|
|
);
|
|
|
|
if (wifitripFileInput && wifitripFileName) {
|
|
wifitripFileInput.addEventListener(
|
|
"change",
|
|
function () {
|
|
if (this.files.length > 0) {
|
|
wifitripFileName.textContent =
|
|
this.files[0].name;
|
|
} else {
|
|
wifitripFileName.textContent =
|
|
"Файл не выбран";
|
|
}
|
|
}
|
|
);
|
|
}
|
|
</script>
|
|
|
|
</section>
|
|
"""
|
|
|
|
html += _validation_result(
|
|
validation
|
|
)
|
|
|
|
html += _import_result(
|
|
import_result
|
|
)
|
|
|
|
html += page_end()
|
|
|
|
return html
|