Add JSON export to Qt/GTK UI
Change-Id: I5ff46a40cdb1f8f41565d2aa54c6f9e61d397e3a Reviewed-on: https://code.wireshark.org/review/16013 Petri-Dish: Pascal Quantin <pascal.quantin@gmail.com> Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org> Reviewed-by: Pascal Quantin <pascal.quantin@gmail.com> Reviewed-by: Alexis La Goutte <alexis.lagoutte@gmail.com> Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
parent
bea1950d07
commit
92f4c7c8d2
80
file.c
80
file.c
@ -2530,6 +2530,7 @@ cf_print_packets(capture_file *cf, print_args_t *print_args,
|
||||
typedef struct {
|
||||
FILE *fh;
|
||||
epan_dissect_t edt;
|
||||
print_args_t *print_args;
|
||||
} write_packet_callback_args_t;
|
||||
|
||||
static gboolean
|
||||
@ -2568,6 +2569,7 @@ cf_write_pdml_packets(capture_file *cf, print_args_t *print_args)
|
||||
}
|
||||
|
||||
callback_args.fh = fh;
|
||||
callback_args.print_args = print_args;
|
||||
epan_dissect_init(&callback_args.edt, cf->epan, TRUE, TRUE);
|
||||
|
||||
/* Iterate through the list of packets, printing the packets we were
|
||||
@ -2646,6 +2648,7 @@ cf_write_psml_packets(capture_file *cf, print_args_t *print_args)
|
||||
}
|
||||
|
||||
callback_args.fh = fh;
|
||||
callback_args.print_args = print_args;
|
||||
|
||||
/* Fill in the column information, only create the protocol tree
|
||||
if having custom columns or field extractors. */
|
||||
@ -2727,6 +2730,7 @@ cf_write_csv_packets(capture_file *cf, print_args_t *print_args)
|
||||
}
|
||||
|
||||
callback_args.fh = fh;
|
||||
callback_args.print_args = print_args;
|
||||
|
||||
/* only create the protocol tree if having custom columns or field extractors. */
|
||||
proto_tree_needed = have_custom_cols(&cf->cinfo) || have_field_extractors();
|
||||
@ -2794,6 +2798,7 @@ cf_write_carrays_packets(capture_file *cf, print_args_t *print_args)
|
||||
}
|
||||
|
||||
callback_args.fh = fh;
|
||||
callback_args.print_args = print_args;
|
||||
epan_dissect_init(&callback_args.edt, cf->epan, TRUE, TRUE);
|
||||
|
||||
/* Iterate through the list of packets, printing the packets we were
|
||||
@ -2822,6 +2827,81 @@ cf_write_carrays_packets(capture_file *cf, print_args_t *print_args)
|
||||
return CF_PRINT_OK;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
write_json_packet(capture_file *cf, frame_data *fdata,
|
||||
struct wtap_pkthdr *phdr, const guint8 *pd,
|
||||
void *argsp)
|
||||
{
|
||||
write_packet_callback_args_t *args = (write_packet_callback_args_t *)argsp;
|
||||
|
||||
/* Create the protocol tree, but don't fill in the column information. */
|
||||
epan_dissect_run(&args->edt, cf->cd_t, phdr, frame_tvbuff_new(fdata, pd), fdata, NULL);
|
||||
|
||||
/* Write out the information in that tree. */
|
||||
write_json_proto_tree(args->print_args, NULL, &args->edt, args->fh);
|
||||
|
||||
epan_dissect_reset(&args->edt);
|
||||
|
||||
return !ferror(args->fh);
|
||||
}
|
||||
|
||||
cf_print_status_t
|
||||
cf_write_json_packets(capture_file *cf, print_args_t *print_args)
|
||||
{
|
||||
write_packet_callback_args_t callback_args;
|
||||
FILE *fh;
|
||||
psp_return_t ret;
|
||||
|
||||
fh = ws_fopen(print_args->file, "w");
|
||||
if (fh == NULL)
|
||||
return CF_PRINT_OPEN_ERROR; /* attempt to open destination failed */
|
||||
|
||||
write_json_preamble(fh);
|
||||
if (ferror(fh)) {
|
||||
fclose(fh);
|
||||
return CF_PRINT_WRITE_ERROR;
|
||||
}
|
||||
|
||||
callback_args.fh = fh;
|
||||
callback_args.print_args = print_args;
|
||||
epan_dissect_init(&callback_args.edt, cf->epan, TRUE, TRUE);
|
||||
|
||||
/* Iterate through the list of packets, printing the packets we were
|
||||
told to print. */
|
||||
ret = process_specified_records(cf, &print_args->range, "Writing PDML",
|
||||
"selected packets", TRUE,
|
||||
write_json_packet, &callback_args, TRUE);
|
||||
|
||||
epan_dissect_cleanup(&callback_args.edt);
|
||||
|
||||
switch (ret) {
|
||||
|
||||
case PSP_FINISHED:
|
||||
/* Completed successfully. */
|
||||
break;
|
||||
|
||||
case PSP_STOPPED:
|
||||
/* Well, the user decided to abort the printing. */
|
||||
break;
|
||||
|
||||
case PSP_FAILED:
|
||||
/* Error while printing. */
|
||||
fclose(fh);
|
||||
return CF_PRINT_WRITE_ERROR;
|
||||
}
|
||||
|
||||
write_json_finale(fh);
|
||||
if (ferror(fh)) {
|
||||
fclose(fh);
|
||||
return CF_PRINT_WRITE_ERROR;
|
||||
}
|
||||
|
||||
/* XXX - check for an error */
|
||||
fclose(fh);
|
||||
|
||||
return CF_PRINT_OK;
|
||||
}
|
||||
|
||||
gboolean
|
||||
cf_find_packet_protocol_tree(capture_file *cf, const char *string,
|
||||
search_direction dir)
|
||||
|
9
file.h
9
file.h
@ -462,6 +462,15 @@ cf_print_status_t cf_write_csv_packets(capture_file *cf, print_args_t *print_arg
|
||||
*/
|
||||
cf_print_status_t cf_write_carrays_packets(capture_file *cf, print_args_t *print_args);
|
||||
|
||||
/**
|
||||
* Print (export) the capture file into JSON format.
|
||||
*
|
||||
* @param cf the capture file
|
||||
* @param print_args the arguments what and how to export
|
||||
* @return one of cf_print_status_t
|
||||
*/
|
||||
cf_print_status_t cf_write_json_packets(capture_file *cf, print_args_t *print_args);
|
||||
|
||||
/**
|
||||
* Find packet with a protocol tree item that contains a specified text string.
|
||||
*
|
||||
|
@ -46,7 +46,8 @@ typedef enum {
|
||||
export_type_csv,
|
||||
export_type_psml,
|
||||
export_type_pdml,
|
||||
export_type_carrays
|
||||
export_type_carrays,
|
||||
export_type_json
|
||||
} export_type_e;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -231,6 +231,13 @@ extern void export_csv_cmd_cb(GtkWidget *widget, gpointer data);
|
||||
*/
|
||||
extern void export_carrays_cmd_cb(GtkWidget *widget, gpointer data);
|
||||
|
||||
/** User requested "Export as JSON" by menu.
|
||||
*
|
||||
* @param widget parent widget (unused)
|
||||
* @param data unused
|
||||
*/
|
||||
extern void export_json_cmd_cb(GtkWidget *widget, gpointer data);
|
||||
|
||||
/** User requested "Expand Tree" by menu.
|
||||
*
|
||||
* @param widget parent widget (unused)
|
||||
|
@ -813,6 +813,7 @@ static const char *ui_desc_menubar =
|
||||
" <separator/>\n"
|
||||
" <menuitem name='AsPSML' action='/File/ExportPacketDissections/PSML'/>\n"
|
||||
" <menuitem name='AsPDML' action='/File/ExportPacketDissections/PDML'/>\n"
|
||||
" <menuitem name='AsJSON' action='/File/ExportPacketDissections/JSON'/>\n"
|
||||
" <separator/>\n"
|
||||
" </menu>\n"
|
||||
" <menuitem name='ExportSelectedPacketBytes' action='/File/ExportSelectedPacketBytes'/>\n"
|
||||
@ -1275,6 +1276,8 @@ static const GtkActionEntry main_menu_bar_entries[] = {
|
||||
NULL, NULL, G_CALLBACK(export_psml_cmd_cb) },
|
||||
{ "/File/ExportPacketDissections/PDML", NULL, "as XML - \"P_DML\" (packet details) file...",
|
||||
NULL, NULL, G_CALLBACK(export_pdml_cmd_cb) },
|
||||
{ "/File/ExportPacketDissections/JSON", NULL, "as \"_JSON\" file...",
|
||||
NULL, NULL, G_CALLBACK(export_json_cmd_cb) },
|
||||
{ "/File/ExportObjects/HTTP", NULL, "_HTTP", NULL, NULL, G_CALLBACK(eo_http_cb) },
|
||||
{ "/File/ExportObjects/DICOM", NULL, "_DICOM", NULL, NULL, G_CALLBACK(eo_dicom_cb) },
|
||||
{ "/File/ExportObjects/SMB", NULL, "_SMB/SMB2", NULL, NULL, G_CALLBACK(eo_smb_cb) },
|
||||
|
@ -60,7 +60,8 @@ typedef enum {
|
||||
output_action_export_psml, /* export to packet summary markup language */
|
||||
output_action_export_pdml, /* export to packet data markup language */
|
||||
output_action_export_csv, /* export to csv file */
|
||||
output_action_export_carrays /* export to C array file */
|
||||
output_action_export_carrays, /* export to C array file */
|
||||
output_action_export_json /* export to json */
|
||||
} output_action_e;
|
||||
|
||||
|
||||
@ -89,6 +90,7 @@ static void print_destroy_cb(GtkWidget *win, gpointer user_data);
|
||||
#define PRINT_PSML_RB_KEY "printer_psml_radio_button"
|
||||
#define PRINT_CSV_RB_KEY "printer_csv_radio_button"
|
||||
#define PRINT_CARRAYS_RB_KEY "printer_carrays_radio_button"
|
||||
#define PRINT_JSON_RB_KEY "printer_json_radio_button"
|
||||
#define PRINT_DEST_CB_KEY "printer_destination_check_button"
|
||||
|
||||
#define PRINT_SUMMARY_CB_KEY "printer_summary_check_button"
|
||||
@ -491,6 +493,60 @@ export_carrays_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Keep a static pointer to the current "Export JSON" window, if any, so that if
|
||||
* somebody tries to do "File:Export to JSON" while there's already a "Export JSON" window
|
||||
* up, we just pop up the existing one, rather than creating a new one.
|
||||
*/
|
||||
static GtkWidget *export_json_win = NULL;
|
||||
|
||||
static print_args_t export_json_args;
|
||||
|
||||
static gboolean export_json_prefs_init = FALSE;
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
void
|
||||
export_json_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
|
||||
{
|
||||
win32_export_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)), &cfile, export_type_json);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
void
|
||||
export_json_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
|
||||
{
|
||||
print_args_t *args = &export_json_args;
|
||||
|
||||
if (export_json_win != NULL) {
|
||||
/* There's already a "Export JSON" dialog box; reactivate it. */
|
||||
reactivate_window(export_json_win);
|
||||
return;
|
||||
}
|
||||
|
||||
/* get settings from preferences (and other initial values) only once */
|
||||
if(export_json_prefs_init == FALSE) {
|
||||
export_json_prefs_init = TRUE;
|
||||
args->format = PR_FMT_TEXT; /* XXX */
|
||||
args->to_file = TRUE;
|
||||
args->file = g_strdup("");
|
||||
args->cmd = g_strdup("");
|
||||
args->print_summary = TRUE;
|
||||
args->print_col_headings = TRUE;
|
||||
args->print_dissections = print_dissections_as_displayed;
|
||||
args->print_hex = FALSE;
|
||||
args->print_formfeed = FALSE;
|
||||
}
|
||||
|
||||
/* init the printing range */
|
||||
packet_range_init(&args->range, &cfile);
|
||||
args->range.process_filtered = TRUE;
|
||||
|
||||
export_json_win = open_print_dialog("Wireshark: Export as \"JSON\" file", output_action_export_json, args);
|
||||
g_signal_connect(export_json_win, "destroy", G_CALLBACK(print_destroy_cb), &export_json_win);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
print_browse_file_cb(GtkWidget *file_bt, GtkWidget *file_te)
|
||||
{
|
||||
@ -508,7 +564,7 @@ open_print_dialog(const char *title, output_action_e action, print_args_t *args)
|
||||
GtkWidget *main_vb;
|
||||
|
||||
GtkWidget *printer_fr, *printer_vb, *export_format_lb;
|
||||
GtkWidget *text_rb, *ps_rb, *pdml_rb, *psml_rb, *csv_rb, *carrays_rb;
|
||||
GtkWidget *text_rb, *ps_rb, *pdml_rb, *psml_rb, *csv_rb, *carrays_rb, *json_rb;
|
||||
GtkWidget *printer_grid, *dest_cb;
|
||||
#ifndef _WIN32
|
||||
GtkWidget *cmd_lb, *cmd_te;
|
||||
@ -609,6 +665,14 @@ open_print_dialog(const char *title, output_action_e action, print_args_t *args)
|
||||
gtk_box_pack_start(GTK_BOX(printer_vb), carrays_rb, FALSE, FALSE, 0);
|
||||
/* gtk_widget_show(carrays_rb); */
|
||||
|
||||
json_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(text_rb), "JSON");
|
||||
if (action == output_action_export_json)
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(json_rb), TRUE);
|
||||
gtk_widget_set_tooltip_text(json_rb,
|
||||
"Print output \"JavaScript Object Notation\" (JSON).");
|
||||
gtk_box_pack_start(GTK_BOX(printer_vb), json_rb, FALSE, FALSE, 0);
|
||||
/* gtk_widget_show(carrays_rb); */
|
||||
|
||||
/* printer grid */
|
||||
printer_grid = ws_gtk_grid_new();
|
||||
gtk_box_pack_start(GTK_BOX(printer_vb), printer_grid, FALSE, FALSE, 0);
|
||||
@ -817,6 +881,7 @@ open_print_dialog(const char *title, output_action_e action, print_args_t *args)
|
||||
g_object_set_data(G_OBJECT(ok_bt), PRINT_PSML_RB_KEY, psml_rb);
|
||||
g_object_set_data(G_OBJECT(ok_bt), PRINT_CSV_RB_KEY, csv_rb);
|
||||
g_object_set_data(G_OBJECT(ok_bt), PRINT_CARRAYS_RB_KEY, carrays_rb);
|
||||
g_object_set_data(G_OBJECT(ok_bt), PRINT_JSON_RB_KEY, json_rb);
|
||||
g_object_set_data(G_OBJECT(ok_bt), PRINT_DEST_CB_KEY, dest_cb);
|
||||
#ifndef _WIN32
|
||||
g_object_set_data(G_OBJECT(ok_bt), PRINT_CMD_TE_KEY, cmd_te);
|
||||
@ -944,7 +1009,7 @@ print_ok_cb(GtkWidget *ok_bt, gpointer parent_w)
|
||||
gchar *f_name;
|
||||
gchar *dirname;
|
||||
gboolean export_as_pdml = FALSE, export_as_psml = FALSE;
|
||||
gboolean export_as_csv = FALSE;
|
||||
gboolean export_as_csv = FALSE, export_as_json = FALSE;
|
||||
gboolean export_as_carrays = FALSE;
|
||||
#ifdef _WIN32
|
||||
gboolean win_printer = FALSE;
|
||||
@ -1026,6 +1091,9 @@ print_ok_cb(GtkWidget *ok_bt, gpointer parent_w)
|
||||
button = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), PRINT_CARRAYS_RB_KEY);
|
||||
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button)))
|
||||
export_as_carrays = TRUE;
|
||||
button = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), PRINT_JSON_RB_KEY);
|
||||
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button)))
|
||||
export_as_json = TRUE;
|
||||
|
||||
button = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), PRINT_SUMMARY_CB_KEY);
|
||||
args->print_summary = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button));
|
||||
@ -1070,6 +1138,8 @@ print_ok_cb(GtkWidget *ok_bt, gpointer parent_w)
|
||||
status = cf_write_csv_packets(&cfile, args);
|
||||
else if (export_as_carrays)
|
||||
status = cf_write_carrays_packets(&cfile, args);
|
||||
else if (export_as_json)
|
||||
status = cf_write_json_packets(&cfile, args);
|
||||
else {
|
||||
switch (args->format) {
|
||||
|
||||
|
@ -96,12 +96,14 @@ ExportDissectionDialog::ExportDissectionDialog(QWidget *parent, capture_file *ca
|
||||
<< tr("Comma Separated Values - summary (*.csv)")
|
||||
<< tr("PSML - summary (*.psml, *.xml)")
|
||||
<< tr("PDML - details (*.pdml, *.xml)")
|
||||
<< tr("JSON (*.json)")
|
||||
<< tr("C Arrays - bytes (*.c, *.h)");
|
||||
export_type_map_[name_filters[0]] = export_type_text;
|
||||
export_type_map_[name_filters[1]] = export_type_csv;
|
||||
export_type_map_[name_filters[2]] = export_type_psml;
|
||||
export_type_map_[name_filters[3]] = export_type_pdml;
|
||||
export_type_map_[name_filters[4]] = export_type_carrays;
|
||||
export_type_map_[name_filters[4]] = export_type_json;
|
||||
export_type_map_[name_filters[5]] = export_type_carrays;
|
||||
setNameFilters(name_filters);
|
||||
selectNameFilter(export_type_map_.key(export_type));
|
||||
exportTypeChanged(export_type_map_.key(export_type));
|
||||
@ -206,6 +208,9 @@ int ExportDissectionDialog::exec()
|
||||
case export_type_pdml: /* PDML */
|
||||
status = cf_write_pdml_packets(cap_file_, &print_args_);
|
||||
break;
|
||||
case export_type_json: /* JSON */
|
||||
status = cf_write_json_packets(cap_file_, &print_args_);
|
||||
break;
|
||||
default:
|
||||
return QDialog::Rejected;
|
||||
}
|
||||
|
@ -2023,6 +2023,7 @@ void MainWindow::setMenusForCaptureFile(bool force_disable)
|
||||
main_ui_->actionFileExportAsPDML->setEnabled(enable);
|
||||
main_ui_->actionFileExportAsPlainText->setEnabled(enable);
|
||||
main_ui_->actionFileExportAsPSML->setEnabled(enable);
|
||||
main_ui_->actionFileExportAsJSON->setEnabled(enable);
|
||||
|
||||
main_ui_->actionFileExportPacketBytes->setEnabled(enable);
|
||||
main_ui_->actionFileExportPDU->setEnabled(enable);
|
||||
@ -2047,6 +2048,7 @@ void MainWindow::setMenusForCaptureInProgress(bool capture_in_progress) {
|
||||
main_ui_->actionFileExportAsPDML->setEnabled(capture_in_progress);
|
||||
main_ui_->actionFileExportAsPlainText->setEnabled(capture_in_progress);
|
||||
main_ui_->actionFileExportAsPSML->setEnabled(capture_in_progress);
|
||||
main_ui_->actionFileExportAsJSON->setEnabled(capture_in_progress);
|
||||
|
||||
main_ui_->actionFileExportPacketBytes->setEnabled(capture_in_progress);
|
||||
main_ui_->actionFileExportPDU->setEnabled(capture_in_progress);
|
||||
|
@ -377,6 +377,7 @@ private slots:
|
||||
void on_actionFileExportAsCArrays_triggered();
|
||||
void on_actionFileExportAsPSML_triggered();
|
||||
void on_actionFileExportAsPDML_triggered();
|
||||
void on_actionFileExportAsJSON_triggered();
|
||||
void on_actionFileExportPacketBytes_triggered();
|
||||
void on_actionFileExportObjectsDICOM_triggered();
|
||||
void on_actionFileExportObjectsHTTP_triggered();
|
||||
|
@ -172,6 +172,7 @@
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionFileExportAsPSML"/>
|
||||
<addaction name="actionFileExportAsPDML"/>
|
||||
<addaction name="actionFileExportAsJSON"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuFileExportObjects">
|
||||
<property name="title">
|
||||
@ -1236,6 +1237,11 @@
|
||||
<string>As PDML XML…</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFileExportAsJSON">
|
||||
<property name="text">
|
||||
<string>As JSON…</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFileExportObjectsHTTP">
|
||||
<property name="text">
|
||||
<string>&HTTP…</string>
|
||||
|
@ -1706,6 +1706,11 @@ void MainWindow::on_actionFileExportAsPDML_triggered()
|
||||
exportDissections(export_type_pdml);
|
||||
}
|
||||
|
||||
void MainWindow::on_actionFileExportAsJSON_triggered()
|
||||
{
|
||||
exportDissections(export_type_json);
|
||||
}
|
||||
|
||||
void MainWindow::on_actionFileExportPacketBytes_triggered()
|
||||
{
|
||||
QString file_name;
|
||||
|
@ -64,7 +64,8 @@
|
||||
_T("CSV (Comma Separated Values summary) (*.csv)\0") _T("*.csv\0") \
|
||||
_T("PSML (XML packet summary) (*.psml)\0") _T("*.psml\0") \
|
||||
_T("PDML (XML packet detail) (*.pdml)\0") _T("*.pdml\0") \
|
||||
_T("C Arrays (packet bytes) (*.c)\0") _T("*.c\0")
|
||||
_T("C Arrays (packet bytes) (*.c)\0") _T("*.c\0") \
|
||||
_T("JSON (*.json)\0") _T("*.json\0")
|
||||
|
||||
#define FILE_TYPES_RAW \
|
||||
_T("Raw data (*.bin, *.dat, *.raw)\0") _T("*.bin;*.dat;*.raw\0") \
|
||||
@ -720,6 +721,9 @@ win32_export_file(HWND h_wnd, capture_file *cf, export_type_e export_type) {
|
||||
case export_type_pdml: /* PDML */
|
||||
status = cf_write_pdml_packets(cf, &print_args);
|
||||
break;
|
||||
case export_type_json: /* JSON */
|
||||
status = cf_write_json_packets(cf, &print_args);
|
||||
break;
|
||||
default:
|
||||
g_free( (void *) ofn);
|
||||
return;
|
||||
|
Loading…
x
Reference in New Issue
Block a user