From 92f4c7c8d24d07ec83622e15310e8e8014c734ab Mon Sep 17 00:00:00 2001 From: Pascal Quantin Date: Sun, 19 Jun 2016 16:52:50 +0200 Subject: Add JSON export to Qt/GTK UI Change-Id: I5ff46a40cdb1f8f41565d2aa54c6f9e61d397e3a Reviewed-on: https://code.wireshark.org/review/16013 Petri-Dish: Pascal Quantin Tested-by: Petri Dish Buildbot Reviewed-by: Pascal Quantin Reviewed-by: Alexis La Goutte Reviewed-by: Anders Broman --- file.c | 80 ++++++++++++++++++++++++++++++++++++++ file.h | 9 +++++ ui/file_dialog.h | 3 +- ui/gtk/main.h | 7 ++++ ui/gtk/main_menubar.c | 3 ++ ui/gtk/print_dlg.c | 76 ++++++++++++++++++++++++++++++++++-- ui/qt/export_dissection_dialog.cpp | 7 +++- ui/qt/main_window.cpp | 2 + ui/qt/main_window.h | 1 + ui/qt/main_window.ui | 6 +++ ui/qt/main_window_slots.cpp | 5 +++ ui/win32/file_dlg_win32.c | 6 ++- 12 files changed, 199 insertions(+), 6 deletions(-) diff --git a/file.c b/file.c index 393fc5ed02..dfd452adcc 100644 --- a/file.c +++ b/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) diff --git a/file.h b/file.h index a52f9a454e..165795be59 100644 --- a/file.h +++ b/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. * diff --git a/ui/file_dialog.h b/ui/file_dialog.h index 19a270f338..7003a5881e 100644 --- a/ui/file_dialog.h +++ b/ui/file_dialog.h @@ -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 diff --git a/ui/gtk/main.h b/ui/gtk/main.h index cdf3f52c4a..4c3064f5cd 100644 --- a/ui/gtk/main.h +++ b/ui/gtk/main.h @@ -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) diff --git a/ui/gtk/main_menubar.c b/ui/gtk/main_menubar.c index 9eaeb71655..9ad082c547 100644 --- a/ui/gtk/main_menubar.c +++ b/ui/gtk/main_menubar.c @@ -813,6 +813,7 @@ static const char *ui_desc_menubar = " \n" " \n" " \n" +" \n" " \n" " \n" " \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) }, diff --git a/ui/gtk/print_dlg.c b/ui/gtk/print_dlg.c index ee4cddd111..1c037ce399 100644 --- a/ui/gtk/print_dlg.c +++ b/ui/gtk/print_dlg.c @@ -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) { diff --git a/ui/qt/export_dissection_dialog.cpp b/ui/qt/export_dissection_dialog.cpp index 769393da7e..306da411fd 100644 --- a/ui/qt/export_dissection_dialog.cpp +++ b/ui/qt/export_dissection_dialog.cpp @@ -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; } diff --git a/ui/qt/main_window.cpp b/ui/qt/main_window.cpp index 2cdf69c3ef..39bc693294 100644 --- a/ui/qt/main_window.cpp +++ b/ui/qt/main_window.cpp @@ -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); diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h index 5d13e44cc7..41d7288e5d 100644 --- a/ui/qt/main_window.h +++ b/ui/qt/main_window.h @@ -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(); diff --git a/ui/qt/main_window.ui b/ui/qt/main_window.ui index e4d23f9b83..8126d4a35d 100644 --- a/ui/qt/main_window.ui +++ b/ui/qt/main_window.ui @@ -172,6 +172,7 @@ + @@ -1236,6 +1237,11 @@ As PDML XML… + + + As JSON… + + &HTTP… diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp index 4d4709bba0..017eaa5d02 100644 --- a/ui/qt/main_window_slots.cpp +++ b/ui/qt/main_window_slots.cpp @@ -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; diff --git a/ui/win32/file_dlg_win32.c b/ui/win32/file_dlg_win32.c index 9d9526c382..ced6304817 100644 --- a/ui/win32/file_dlg_win32.c +++ b/ui/win32/file_dlg_win32.c @@ -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; -- cgit v1.2.1