Add JSON export to Qt/GTK UI
authorPascal Quantin <pascal.quantin@gmail.com>
Sun, 19 Jun 2016 14:52:50 +0000 (16:52 +0200)
committerAnders Broman <a.broman58@gmail.com>
Mon, 20 Jun 2016 01:22:57 +0000 (01:22 +0000)
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>
12 files changed:
file.c
file.h
ui/file_dialog.h
ui/gtk/main.h
ui/gtk/main_menubar.c
ui/gtk/print_dlg.c
ui/qt/export_dissection_dialog.cpp
ui/qt/main_window.cpp
ui/qt/main_window.h
ui/qt/main_window.ui
ui/qt/main_window_slots.cpp
ui/win32/file_dlg_win32.c

diff --git a/file.c b/file.c
index 393fc5ed022da068db555a55c9222eb65ae17ddf..dfd452adcc19134581a6562f6100f1660f4ca6ba 100644 (file)
--- 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 a52f9a454e4d5937d709919fb880c6fa59191f9e..165795be59668562a87f52598b10bbdf247f1815 100644 (file)
--- 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.
  *
index 19a270f338a3b2aec539eac4d4c85de1dd84eb0a..7003a5881ebbb605cd314a53d58573a6225c4983 100644 (file)
@@ -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
index cdf3f52c4a18936daa8c4f336da7816a99d88060..4c3064f5cd6d127e001ee06c62520bcb60f7d4b5 100644 (file)
@@ -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)
index 9eaeb71655bd1d3851f30b902ee1ec34e63d8abc..9ad082c547e86dd3d7db6e9ff62e4abdd55f5239 100644 (file)
@@ -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) },
index ee4cddd111174ddca1e3cd7049893a2424458f14..1c037ce399572f900a30b25e1fd4399afdeee19e 100644 (file)
@@ -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) {
 
index 769393da7ed3d95768f007bda76fd1a70904391d..306da411fd2264af2a336b8ca54f9ec19bf00c3c 100644 (file)
@@ -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;
         }
index 2cdf69c3ef141810e65ac5d2e31720bbe91431b8..39bc6932941c9b318562535fc67d7dc235fded1f 100644 (file)
@@ -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);
index 5d13e44cc7a8a36b148c2bfd970d846fec6c7a82..41d7288e5d7d2e6a3b05782469df2f81da1e4386 100644 (file)
@@ -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();
index e4d23f9b83c35170379b167fb84de10771293868..8126d4a35d6ce5b2ee9a179cc9b488b0cc223dbd 100644 (file)
      <addaction name="separator"/>
      <addaction name="actionFileExportAsPSML"/>
      <addaction name="actionFileExportAsPDML"/>
+     <addaction name="actionFileExportAsJSON"/>
     </widget>
     <widget class="QMenu" name="menuFileExportObjects">
      <property name="title">
     <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>&amp;HTTP…</string>
index 4d4709bba0f53d352219f7c8c38e9021f73622f2..017eaa5d0264efe1e45c43ed3439680816ea80d8 100644 (file)
@@ -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;
index 9d9526c3829b492a726836d3c0efa706e90c9918..ced6304817d3110817b2ab531f7c07c956737cfc 100644 (file)
@@ -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;