/*
    ettercap -- gtk interface for connection list

    Copyright (C) 2001  ALoR <alor@users.sourceforge.net>, NaGA <crwm@freemail.it>
    GTK+ 2.0 interface by daten <daten@dnetc.org>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <ec_main.h>

#if defined(HAVE_SYS_POLL_H)
   #include <sys/poll.h>
#elif defined (HAVE_POLL_H)
   #include <poll.h>
#endif

#include <ec_gtk.h>
#include <ec_gtk_sniff_data.h>
#include <ec_gtk_factory.h>
#include <ec_gtk_inject.h>
#include <ec_gtk_passive.h>
#include <ec_gtk_icons.h>
#include <ec_filterdrop.h>
#include <ec_decodedata.h>
#include <ec_illithid.h>
#include <ec_doppleganger.h>
#include <ec_grell.h>
#include <ec_inet.h>
#include <ec_inet_structures.h>
#include <ec_buffer.h>
#include <ec_logtofile.h>
#include <ec_thread.h>

#ifdef PERMIT_PLUGINS
   #include <ec_gtk_plugins.h>
   #include <ec_plugins.h>
#endif


// protos...
void Ginterface_Sniff_Run(short mode);
void Ginterface_Sniff_PointItem(GtkWidget *widget, gpointer data);
void Ginterface_Sniff_SelectItem(GtkTreeView *treeview, gpointer arg1, GtkTreeViewColumn *arg2, gpointer data);
void Ginterface_Sniff_InitList(void);
gboolean Ginterface_Sniff_RefreshList(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data);
void Ginterface_Sniff_ClearList(void);
void Ginterface_Sniff_KillConn(void);
void Ginterface_Sniff_ActiveDissector(short mode);

gboolean Ginterface_Sniff_WatchList(gpointer data);
void Ginterface_Sniff_ToolBar(GtkItemFactory *host_menu);
void Ginterface_Sniff_Callback(gpointer data, guint action, GtkWidget *widget);
void Ginterface_Sniff_Stop(void);

GtkListStore *s_list;
GtkTreeIter s_iter;
GtkWidget *s_treeview = NULL;
GtkTreeSelection *s_selection = NULL;
GtkWidget *s_scrolled = NULL;

int gConn_Base_Pointer = 0;
int gConn_Pointer = 0;
int smode;     // a global (to this file) mode variable
int id_watch;
extern int Sel_Number;
extern short ginject;
extern char read_from_dump_flag; // from inet

pthread_t Illithid_pid = 0, Dopple_pid = 0, time_pid = 0;
#if defined (HAVE_OPENSSL) && defined (PERMIT_HTTPS)
   pthread_t Grell_pid = 0;
#endif

//---------------------------

void Ginterface_Sniff_InitList(void)
{
   int j;

   g_signal_handlers_block_by_func (G_OBJECT (s_selection), G_CALLBACK (Ginterface_Sniff_PointItem), NULL);

   if(!s_list) {                   /* number, hostname, ip, divider, hostname, ip, status, type */
      s_list = gtk_list_store_new (8, G_TYPE_INT, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING);
      gtk_tree_view_set_model (GTK_TREE_VIEW (s_treeview), GTK_TREE_MODEL (s_list));
      // g_signal_connect (G_OBJECT (s_list), "row-inserted", G_CALLBACK (Ginterface_ScrollToBottom), (gpointer)s_scrolled);
   }

   for(j = gConn_Base_Pointer; j < number_of_connections; j++)
   {
      gtk_list_store_append (s_list, &s_iter);
      gtk_list_store_set (s_list, &s_iter, 
            0, j+1,
            1, Conn_Between_Hosts[j].source_ip,
            2, Conn_Between_Hosts[j].source_port,
            3, "<-->",
            4, Conn_Between_Hosts[j].dest_ip,
            5, Conn_Between_Hosts[j].dest_port,
            6, Conn_Between_Hosts[j].status,
            7, Conn_Between_Hosts[j].type, -1);
      ++gConn_Base_Pointer;
   }

   g_signal_handlers_unblock_by_func (G_OBJECT (s_selection), G_CALLBACK (Ginterface_Sniff_PointItem), NULL);

   gtk_tree_model_foreach(GTK_TREE_MODEL (s_list), Ginterface_Sniff_RefreshList, NULL);
}

gboolean Ginterface_Sniff_RefreshList(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
{
   int line, j;
   gchar status[8], type[18];

   gtk_tree_model_get (model, iter,
                        0, &line,
                        6, status,
                        7, type, -1);

   j = line - 1;

   if((strcmp(status, Conn_Between_Hosts[j].status) == 0) &&
      (strcmp(type, Conn_Between_Hosts[j].type) == 0))
      return(FALSE);
   
   gtk_list_store_set (GTK_LIST_STORE (model), iter,
            0, j+1,
            6, Conn_Between_Hosts[j].status,
            7, Conn_Between_Hosts[j].type, -1);

   while (g_main_iteration(FALSE)); /* update gui */
   
   return(FALSE);
}

void Ginterface_Sniff_PointItem(GtkWidget *widget, gpointer data)
{
   GtkTreeIter iter;
   GtkTreeModel *model;
   gchar temp[21];
   int line;

   if (gtk_tree_selection_get_selected (GTK_TREE_SELECTION (widget), &model, &iter)) {
      gtk_tree_model_get (model, &iter, 0, &line, -1);
      // line = atoi(strtok(lineptr, ")"));
   } else return;

   gConn_Pointer = line - 1;

   if(strlen(Conn_Between_Hosts[gConn_Pointer].user) < 20)
      Ginterface_EditBottom(0, Conn_Between_Hosts[gConn_Pointer].user);
   else {
      snprintf(temp, 20, "%s->", Conn_Between_Hosts[gConn_Pointer].user);
      Ginterface_EditBottom(0, temp);
   }

   if(strlen(Conn_Between_Hosts[gConn_Pointer].pass) < 20)
      Ginterface_EditBottom(1, Conn_Between_Hosts[gConn_Pointer].pass);
   else {
      snprintf(temp, 20, "%s->", Conn_Between_Hosts[gConn_Pointer].pass);
      Ginterface_EditBottom(1, temp);
   }
}

void Ginterface_Sniff_SelectItem(GtkTreeView *treeview, gpointer arg1, GtkTreeViewColumn *arg2, gpointer data)
{
   Ginterface_Sniff_Callback(NULL, (guint) '!', GTK_WIDGET (treeview));
}

void Ginterface_Sniff_ClearList(void)
{
   Ginterface_PopUpN("updating list...");

   if(s_list)
      gtk_list_store_clear(s_list);
   Decodedata_RefreshConnectionList();
   gConn_Pointer = 0;
   gConn_Base_Pointer = 0;

   Ginterface_PopUpN(NULL);   /* destroys last popup notice */

   DEBUG_MSG("Ginterface_Sniff_ClearList -- end");
}


void Ginterface_Sniff_KillConn(void)
{
   KILL_DATA kill_data;

   if (Conn_Between_Hosts[gConn_Pointer].proto == 'U')
   {
      Ginterface_PopUp("Trying to kill an UDP connection ?!? Hey Kiddie, go home !!");
      return;
   }

   DEBUG_MSG("Ginterface_Sniff_KillConn -- %s:%d -> %s:%d ", Conn_Between_Hosts[gConn_Pointer].source_ip,
                                                            Conn_Between_Hosts[gConn_Pointer].source_port,
                                                            Conn_Between_Hosts[gConn_Pointer].dest_ip,
                                                            Conn_Between_Hosts[gConn_Pointer].dest_port );

   kill_data.source_ip = inet_addr(Conn_Between_Hosts[gConn_Pointer].source_ip);
   kill_data.dest_ip = inet_addr(Conn_Between_Hosts[gConn_Pointer].dest_ip);
   kill_data.source_port = htons(Conn_Between_Hosts[gConn_Pointer].source_port);
   kill_data.dest_port = htons(Conn_Between_Hosts[gConn_Pointer].dest_port);
   write(pipe_kill[1], &kill_data, sizeof(KILL_DATA));

   strcpy(Conn_Between_Hosts[gConn_Pointer].status, "KILLED");

}


void Ginterface_Sniff_ActiveDissector(short mode)
{

   DEBUG_MSG("Ginterface_Sniff_ActiveDissector %d", active_dissector);

   Ginterface_EditTop(8, "Active Dissector: ");

   switch(active_dissector)
   {
      case 1:  Ginterface_AppendTop(8, "ON");
               if (mode == PUBLICARP) Ginterface_AppendTop(8, " (only PUBLIC ARP)");
               break;

      case 0:  Ginterface_AppendTop(8, "OFF");
               if (mode == PUBLICARP) Ginterface_AppendTop(8, " (only PUBLIC ARP)");
               break;
   }
}



void Ginterface_Sniff_Run(short mode)
{
   GtkCellRenderer *s_renderer;
   GtkTreeViewColumn *s_column;
   int i;

   smode = mode;

   DEBUG_MSG("Ginterface_Sniff_Run -- mode %d", mode);

   if (pipe_with_illithid_data == -1) pipe_with_illithid_data = Buffer_Create(1.0e6);
   if (pipe_with_plugins == -1) pipe_with_plugins = Buffer_Create(1.5e5);  // 150 Kbyte
   
   Ginterface_ClearBottom();

   if (mode > PUBLICARP )
      active_dissector = 0;

   Ginterface_Sniff_ActiveDissector(mode);
   Ginterface_Inject_FilterTopStatus();

   number_of_connections = 0;
   Ginterface_Sniff_ClearList();

   /* make the scrolled window */
   s_scrolled = gtk_scrolled_window_new(NULL, NULL);
   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (s_scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (s_scrolled), GTK_SHADOW_IN);
   s_treeview = gtk_tree_view_new ();
   gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (s_treeview), FALSE);
      /* add the column */
   s_renderer = gtk_cell_renderer_text_new ();
   s_column = gtk_tree_view_column_new_with_attributes ("#", s_renderer, "text", 0, NULL);
   gtk_tree_view_column_set_sort_column_id (s_column, 0);
   gtk_tree_view_column_set_resizable (s_column, FALSE);
   gtk_tree_view_append_column (GTK_TREE_VIEW(s_treeview), s_column);
      /* add the column */
   s_renderer = gtk_cell_renderer_text_new ();
   s_column = gtk_tree_view_column_new_with_attributes ("Source", s_renderer, "text", 1, NULL);
   gtk_tree_view_column_set_sort_column_id (s_column, 1);
   gtk_tree_view_column_set_resizable (s_column, TRUE);
   gtk_tree_view_append_column (GTK_TREE_VIEW(s_treeview), s_column);
      /* add the column */
   s_renderer = gtk_cell_renderer_text_new ();
   s_column = gtk_tree_view_column_new_with_attributes ("Port", s_renderer, "text", 2, NULL);
   gtk_tree_view_column_set_sort_column_id (s_column, 2);
   gtk_tree_view_column_set_resizable (s_column, TRUE);
   gtk_tree_view_append_column (GTK_TREE_VIEW(s_treeview), s_column);
      /* add the column */
   s_renderer = gtk_cell_renderer_text_new ();
   s_column = gtk_tree_view_column_new_with_attributes ("<-->", s_renderer, "text", 3, NULL);
   gtk_tree_view_column_set_sort_column_id (s_column, 3);
   gtk_tree_view_column_set_resizable (s_column, FALSE);
   gtk_tree_view_append_column (GTK_TREE_VIEW(s_treeview), s_column);
      /* add the column */
   s_renderer = gtk_cell_renderer_text_new ();
   s_column = gtk_tree_view_column_new_with_attributes ("Destination", s_renderer, "text", 4, NULL);
   gtk_tree_view_column_set_sort_column_id (s_column, 4);
   gtk_tree_view_column_set_resizable (s_column, TRUE);
   gtk_tree_view_append_column (GTK_TREE_VIEW(s_treeview), s_column);
      /* add the column */
   s_renderer = gtk_cell_renderer_text_new ();
   s_column = gtk_tree_view_column_new_with_attributes ("Port", s_renderer, "text", 5, NULL);
   gtk_tree_view_column_set_sort_column_id (s_column, 5);
   gtk_tree_view_column_set_resizable (s_column, TRUE);
   gtk_tree_view_append_column (GTK_TREE_VIEW(s_treeview), s_column);
      /* add the column */
   s_renderer = gtk_cell_renderer_text_new ();
   s_column = gtk_tree_view_column_new_with_attributes ("Status", s_renderer, "text", 6, NULL);
   gtk_tree_view_column_set_sort_column_id (s_column, 6);
   gtk_tree_view_column_set_resizable (s_column, TRUE);
   gtk_tree_view_append_column (GTK_TREE_VIEW(s_treeview), s_column);
      /* add the column */
   s_renderer = gtk_cell_renderer_text_new ();
   s_column = gtk_tree_view_column_new_with_attributes ("Type", s_renderer, "text", 7, NULL);
   gtk_tree_view_column_set_sort_column_id (s_column, 7);
   gtk_tree_view_column_set_resizable (s_column, TRUE);
   gtk_tree_view_append_column (GTK_TREE_VIEW(s_treeview), s_column);
      /* connect signals */
   s_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (s_treeview));
   gtk_tree_selection_set_mode (s_selection, GTK_SELECTION_SINGLE);
   g_signal_connect (G_OBJECT (s_selection), "changed", G_CALLBACK (Ginterface_Sniff_PointItem), NULL);
   g_signal_connect (G_OBJECT (s_treeview), "row_activated", G_CALLBACK (Ginterface_Sniff_SelectItem), NULL);
      /* add it and display it */
   gtk_container_add(GTK_CONTAINER (s_scrolled), s_treeview);
   gtk_widget_show(s_treeview);

   Ginterface_Sniff_InitList();

   switch (mode)
   {
      case ARPBASED:
      case PUBLICARP:
                     gtk_container_add(GTK_CONTAINER (mainbox[1]), s_scrolled);
                     gtk_widget_show (s_scrolled);
                     Inet_DisableForwarding();

                     for(i=0; i<number_of_hosts_in_lan; i++)
                     {
                        if ( !strcmp(Host_Source.ip, Host_In_LAN[i].ip) )
                           strlcpy(Host_Source.mac, Host_In_LAN[i].mac, sizeof(Host_Source.mac));
                        if ( !strcmp(Host_Dest.ip, Host_In_LAN[i].ip) )
                           strlcpy(Host_Dest.mac, Host_In_LAN[i].mac, sizeof(Host_Dest.mac));
                     }

                     if ( !strcmp(Host_Source.mac, "") && !strcmp(Host_Dest.mac, "") )
                        Error_msg("Doppelganger needs at least one valid mac address !!\n\n(the ip was not found in the list)");

                     Dopple_pid = Doppleganger_Run(Options.netiface, Host_Source.ip, Host_Dest.ip, Host_Source.mac, Host_Dest.mac);

                     if ( mode == ARPBASED )
                        Illithid_pid = Illithid_ARPBased_GetConnections(Options.netiface, Host_Source.ip, Host_Dest.ip, Host_Source.mac, Host_Dest.mac);
                     else
                        Illithid_pid = Illithid_PublicARP_GetConnections(Options.netiface, Host_Source.ip, Host_Dest.ip, Host_Source.mac, Host_Dest.mac);

                     #if defined (HAVE_OPENSSL) && defined (PERMIT_HTTPS)
                        Grell_pid = Grell_Run();
                     #endif

                     break;
      case IPBASED:
                     gtk_container_add(GTK_CONTAINER (mainbox[2]), s_scrolled);
                     gtk_widget_show (s_scrolled);
                     Illithid_pid = Illithid_IPBased_GetConnections(Options.netiface, Host_Source.ip, Host_Dest.ip);
                     break;
      case MACBASED:
                     gtk_container_add(GTK_CONTAINER (mainbox[3]), s_scrolled);
                     gtk_widget_show (s_scrolled);
                     Illithid_pid = Illithid_MACBased_GetConnections(Options.netiface, Host_Source.mac, Host_Dest.mac);
                     break;
   }

   time_pid = ECThread_create("timeouter", &DecodeData_Connection_Timeouter, NULL);

   id_watch = g_timeout_add(300, Ginterface_Sniff_WatchList, NULL);
}

gboolean Ginterface_Sniff_WatchList(gpointer data)
{
   if (conn_list_modified) {
      Ginterface_Sniff_InitList();
      conn_list_modified = 0;
   }

#ifdef PERMIT_PLUGINS
   Ginterface_Plugins_Output_Update();
#endif

   if(number_of_connections < 0)
      return FALSE;
   else
      return TRUE;
}

void Ginterface_Sniff_ToolBar(GtkItemFactory *host_menu) {
   static GtkItemFactoryEntry action_menu[] = {
      MENU_COMMON
      { "/Action", NULL, NULL, 0, "<Branch>", NULL},
      { "/Action/Packet Forge", "x", Ginterface_Sniff_Callback, 'x', "<StockItem>", GTK_STOCK_PREFERENCES},
      { "/Action/Active Password Collectors", "a", Ginterface_Sniff_Callback, 'a', "<ToggleItem>", NULL},
      { "/Action/Filter Chains", "f", Ginterface_Sniff_Callback, 'f', "<ImageItem>", chain_icon},
      { "/Action/Log Passwords to File", "l", Ginterface_Sniff_Callback, 'l', "<StockItem>", GTK_STOCK_SAVE},
      { "/Action/Kill Connection", "k", Ginterface_Sniff_Callback, 'k', "<ImageItem>", kill_icon},
#ifdef PERMIT_PLUGINS
      { "/Action/Plugin Output", "i", Ginterface_Sniff_Callback, 'i', "<StockItem>", GTK_STOCK_JUMP_TO},
#endif
      { "/Action/Resolve IP", "d", Ginterface_Sniff_Callback, 'd', "<StockItem>", GTK_STOCK_CONVERT},
      { "/Action/Refresh List", "r", Ginterface_Sniff_Callback, 'r', "<StockItem>", GTK_STOCK_REFRESH},
      { "/Help", NULL, NULL, 0, "<LastBranch>", NULL},
      { "/Help/_Manual", NULL, Ginterface_Manual, 0, "<StockItem>", GTK_STOCK_HELP},
      { "/Help/_About", NULL, Ginterface_About, 0, "<StockItem>", GTK_STOCK_DIALOG_INFO}
   };
   gint num_items = 0;

   DEBUG_MSG("Ginterface_Sniff_ToolBar");

   gtk_item_factory_delete_item(host_menu, "/Quit");
   gtk_item_factory_delete_item(host_menu, "/Sniff");
   gtk_item_factory_delete_item(host_menu, "/View");
   gtk_item_factory_delete_item(host_menu, "/Action");
   gtk_item_factory_delete_item(host_menu, "/Help");

   num_items = sizeof (action_menu) / sizeof (action_menu[0]);
   gtk_item_factory_create_items (host_menu, num_items, action_menu, NULL);
}

void Ginterface_Sniff_Callback(gpointer data, guint action, GtkWidget *widget) {
   switch (action) {
      case '!':
         if (Conn_Between_Hosts[gConn_Pointer].source_ip == NULL) break;   // no connections

         Connection_Mode = 0;

         g_object_ref(G_OBJECT (frame[1]));
         gtk_container_remove(GTK_CONTAINER (ec_table), frame[1]);

         Ginterface_Sniff_Data_ToolBar(main_menu);

         Ginterface_Sniff_Data_Run(Conn_Between_Hosts[gConn_Pointer].source_ip,
                                  Conn_Between_Hosts[gConn_Pointer].source_port,
                                  Conn_Between_Hosts[gConn_Pointer].dest_ip,
                                  Conn_Between_Hosts[gConn_Pointer].dest_port,
                                  Conn_Between_Hosts[gConn_Pointer].source_mac,
                                  Conn_Between_Hosts[gConn_Pointer].dest_mac,
                                  Conn_Between_Hosts[gConn_Pointer].proto,
                                  Conn_Between_Hosts[gConn_Pointer].type,
                                  smode, gConn_Pointer);
         break;
      case 'K':
      case 'k':
         if (Conn_Between_Hosts[gConn_Pointer].source_ip == NULL) break;   // no connections
         Ginterface_Sniff_KillConn();      // with TH_RST !  i'm too tired to implement FIN handshaking
         Ginterface_Sniff_InitList();
         break;

      case 'D':
      case 'd':
         {
            char source[40];
            char dest[40];
            if (Conn_Between_Hosts[gConn_Pointer].source_ip == NULL) break;   // no connections
            snprintf(source, 39, " %s (%s) <-->", Inet_HostName(Conn_Between_Hosts[gConn_Pointer].source_ip),
                                                  Conn_Between_Hosts[gConn_Pointer].source_ip);
            snprintf(dest, 39, " %s (%s)", Inet_HostName(Conn_Between_Hosts[gConn_Pointer].dest_ip),
                                           Conn_Between_Hosts[gConn_Pointer].dest_ip);
            source[39] = dest[39] = 0;
            Ginterface_PopUp("%s%s", source, dest);
         }
         break;

      case 'R':
      case 'r':
         Ginterface_Sniff_ClearList();
         Ginterface_Sniff_InitList();
         break;

      case 'X':
      case 'x':
         Ginterface_Factory_Run();
         break;

      case 'L':
      case 'l':
         Ginterface_PopUp("Passwords dumped to %s", LogToFile_DumpPass());
         break;

      case 'P':
      case 'p':
#ifdef PERMIT_PLUGINS
         Ginterface_Plugins_Run();
         Buffer_Flush(pipe_with_plugins);
#else
         Ginterface_PopUp("Plugins NOT supported under your system !!");
#endif
         break;
                     
#ifdef PERMIT_PLUGINS
      case 'I':
      case 'i':
         Ginterface_Plugins_Output();
         break;
#endif

      case 'A':
      case 'a':
         if (smode <= PUBLICARP)
         {
            active_dissector = (active_dissector) ? 0 : 1;     // activate/deactivate active dissector (arp based)
            DEBUG_MSG("\tactive_dissector %d", active_dissector);
            Ginterface_Sniff_ActiveDissector(smode);
         }
         else
         {
            Ginterface_PopUp("ACTIVE dissector is available only in ARP mode !!");
         }
         break;

      case 'F':
      case 'f':
         Ginterface_Inject_SetFilter(smode);
         break;
      case 'q':
      case 'Q':
         DEBUG_MSG("Ginterface_Run_END");
         {
            char answer;
            answer = Ginterface_PopUpQ("Do you really want to exit?");
            if ((answer == 'y') || (answer == 'Y'))
               Ginterface_WExit("They are safe!!  for now... ");
         }
         break;
      case 'H':
      case 'h':{
            static char *help[] = {
               "[qQ][F10] - quit",
               "[return]  - sniff the selected connection",
               "[xX]      - Packet Forge",
               "[aA]      - enable/disable ACTIVE password collectors",
               "[fF]      - set/edit filters chains",
               "[lL]      - log all collected passwords to a file",
               "[kK]      - kill the connection (be careful !)",
               "[pP]      - plugin management",
#ifdef PERMIT_PLUGINS
               "[iI]      - plugin output window",
#endif
               "[oO]      - passive scanning of the LAN",
               "[dD]      - resolve ip via DNS",
               "[rR]      - refresh the list",
               NULL};
            Ginterface_HelpWindow(help);
         }
         break;
   }
}

void Ginterface_Sniff_Stop(void) {
         if (ginject == 1)
         {
            char answer;
            answer = Ginterface_PopUpQ("If you exit now some connections may be ReSeT!! continue (y/n)?");
            if ((answer != 'y') && (answer != 'Y'))
               return;
         }

         Ginterface_PopUpN("shutting down all threads...");

         g_source_remove (id_watch);
         ECThread_destroy(Illithid_pid);
         ECThread_destroy(time_pid);
         if (Dopple_pid) ECThread_destroy(Dopple_pid);
         #if defined (HAVE_OPENSSL) && defined (PERMIT_HTTPS)
            if (Grell_pid) ECThread_destroy(Grell_pid);
         #endif

         Ginterface_PopUpN(NULL);

         gdk_flush();
            
         ginject = 0;

         g_free (s_list);
         s_list = NULL;

         DEBUG_MSG("Ginterface_Sniff_END");

         Decodedata_RefreshConnectionList();
         number_of_connections = -1;
         read_from_dump_flag = 0;  // permit a new rescan of the offline file
         return;
}

/* EOF */

// vim:ts=3:expandtab

