#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>

#include <gtk/gtk.h>

#include "callbacks.h"
#include "interface.h"
#include "support.h"

#include "display.h"
#include "capture.h"
#include "crack.h"
#include "bssidlist.h"
#include <pcap.h>

void reallyQuit(void);

#ifndef DLT_PRISM_HEADER
#define DLT_PRISM_HEADER 119
#endif

#ifndef DLT_AIRONET_HEADER
#define DLT_AIRONET_HEADER 120
#endif

int scan = 1;
int oldscan;
int chan = 6;
int spinchan = 6;
int funcId = 0;
int doCapture = 0;
pthread_t capThread;
int breadth40 = 3;
int breadth128 = 2;
int quitting = 0;
int logfile = 0;
char dumpfile[256] = {0};
int cardType = 0;
int realized = 0;

int readPcapFile = 0;

char *cards[] = { "Prism2 (wlan-ng)", "Orinoco (orinoco_cs)", "Other" };
static const int NUM_CARDS = sizeof(cards);

GtkCList *theList;
GdkEventButton *popupEvent;
GtkCheckMenuItem *logFileMenu;
char errbuf[PCAP_ERRBUF_SIZE];

void reallyQuit() {
   doCapture = 0;
   if (logfile != -1) {
      close(logfile);
   }
   saveOpts();
   gtk_main_quit();
}

/* Function to open a dialog box displaying the message provided. */

void quick_message(char *title, char *message) {

   GtkWidget *dialog, *label, *okay_button;

   /* Create the widgets */
   
   dialog = gtk_dialog_new();
   gtk_window_set_title(GTK_WINDOW(dialog), title);
   gtk_window_set_modal(GTK_WINDOW(dialog), 1);
   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
   gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG(dialog)->action_area), 10);
   gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox), 10);

   label = gtk_label_new (message);
   okay_button = gtk_button_new_with_label("Okay");
   
   /* Ensure that the dialog box is destroyed when the user clicks ok. */
   
   gtk_signal_connect_object (GTK_OBJECT (okay_button), "clicked",
                              GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT(dialog));
   gtk_box_pack_start (GTK_BOX (GTK_DIALOG(dialog)->action_area), okay_button, TRUE, FALSE, 0);

   /* Add the label, and show everything we've added to the dialog. */

   gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
                      label);
   gtk_widget_show_all (dialog);
}

void
on_AirSnortWindow_realize              (GtkWidget       *widget,
                                        gpointer         user_data)
{
   theList = (GtkCList*) user_data;
   if (!funcId) {
      funcId = gtk_timeout_add( 500, (GtkFunction) update, user_data); 
   }
}


gboolean
on_AirSnortWindow_delete_event         (GtkWidget       *widget,
                                        GdkEvent        *event,
                                        gpointer         user_data)
{
   if (!head) {
      reallyQuit();
   }
   else {
      gtk_widget_show_all (create_CloseDlg());
   }
  return TRUE;
}


void
on_load_activate                       (GtkMenuItem     *menuitem,
                                        gpointer         user_data)
{
   if (doCapture) {
      quick_message("Error", "Can't load while capture in progress");
      return;
   }
   gtk_widget_show_all (create_FileLoad());
}


void
on_save_activate                       (GtkMenuItem     *menuitem,
                                        gpointer         user_data)
{
   gtk_widget_show_all (create_SaveFile());
}


void
on_exit_activate                       (GtkMenuItem     *menuitem,
                                        gpointer         user_data)
{
   if (!head) {
      reallyQuit();
   }
   else {
      gtk_widget_show_all (create_CloseDlg());
   }
}


void
on_clear_activate                      (GtkMenuItem     *menuitem,
                                        gpointer         user_data)
{
   on_Clear_clicked(NULL, (gpointer)theList);
}

void
on_about_activate                      (GtkMenuItem     *menuitem,
                                        gpointer         user_data)
{
   gtk_widget_show_all (create_AboutDlg());
}

void
on_Start_clicked                       (GtkButton       *button,
                                        gpointer         user_data)
{
   char cmd[80];
   CaptureArg *ca;
   if (doCapture) return;

   if (!scan) {
      chan = spinchan;
   }
   if (cardType == PRISM) {
      sprintf(cmd, "wlanctl-ng %s lnxreq_ifstate ifstate=enable > /dev/null", dev);
      system(cmd);
      sprintf(cmd, "ifconfig %s up", dev);
      system(cmd);
   }
   if (setChannel(chan)) {
      quick_message("Error", "Could not set monitor mode");
      return;
   }  
   doCapture = 1;
   ca = (CaptureArg*) malloc(sizeof(CaptureArg));
   ca->pcap = pcap_open_live(dev, 3000, 1, 0, errbuf);
   if (ca->pcap) {
      switch (pcap_datalink(ca->pcap)) {
         case DLT_PRISM_HEADER:
            ca->offset = 144;
            break;
         case DLT_IEEE802_11:
            ca->offset = 0;
            break;
         case DLT_AIRONET_HEADER:
            ca->offset = 0;
            break;
         default: //COOKED
            ca->offset = 160;
      }
      if (*dumpfile) {
         ca->dump = pcap_dump_open(ca->pcap, dumpfile);
         dumpfile[0] = 0;
      }
      else {
         ca->dump = NULL;
      }
      pthread_create(&capThread, NULL, capture, ca);
   }
}


void
on_Stop_clicked                        (GtkButton       *button,
                                        gpointer         user_data)
{
   doCapture = 0;
}


void
on_freq_changed                        (GtkEditable     *editable,
                                        gpointer         user_data)
{
   spinchan = gtk_spin_button_get_value_as_int((GtkSpinButton*) editable);
   if (!scan) {
      chan = spinchan; 
      if (doCapture) {
         setChannel(chan);
      }
   }
}


void
on_scan_toggled                        (GtkToggleButton *togglebutton,
                                        gpointer         user_data)
{
   scan = gtk_toggle_button_get_active(togglebutton); 
   if (!scan) {
      chan = spinchan; 
      if (doCapture) {
         setChannel(chan);
      }
   }
}


void
on_Clear_clicked                       (GtkButton       *button,
                                        gpointer         user_data)
{
   gtk_clist_clear((GtkCList*) user_data);
   clearList();
}


void
on_DataList_select_row                 (GtkCList        *clist,
                                        gint             row,
                                        gint             column,
                                        GdkEvent        *event,
                                        gpointer         user_data)
{

}

void myPositionFunc(GtkMenu *menu, int *x, int *y, gpointer data) {
   popupEvent = (GdkEventButton*) data;
   *x = popupEvent->x_root;
   *y = popupEvent->y_root;
}

gboolean
on_DataList_button_press_event         (GtkWidget       *widget,
                                        GdkEventButton  *event,
                                        gpointer         user_data)
{
   if (event->button == 3) {
      gtk_menu_popup (GTK_MENU(create_ListPopup()), NULL, NULL, 
                      myPositionFunc, event, event->button, 0);
   }
   return FALSE;
}

void
on_CloseYes_clicked                    (GtkButton       *button,
                                        gpointer         user_data)
{
   quitting = 1;
   gtk_widget_show_all (create_SaveFile());
}

void
on_CloseNo_clicked                     (GtkButton       *button,
                                        gpointer         user_data)
{
   reallyQuit();
}

void
loadPacketFile                         (GtkButton       *button,
                                        gpointer         user_data)
{
   PacketInfo pi;
   CaptureRec cap;
   unsigned char bssid[6], sta[] = {0,0,0,0,0,0};
   unsigned char dummyPacket[200];
   int len;
   unsigned char slen, chan;
   char *fname = gtk_file_selection_get_filename(GTK_FILE_SELECTION(user_data));
   int fd = open(fname, O_RDONLY);
   cap.pInfo = &pi;
   cap.bssid = bssid;
   cap.sta = sta;
   pi.raw = dummyPacket;
   if (fd != -1) {
      while (read(fd, &len, sizeof(int)) == sizeof(int)) {
         if (read(fd, bssid, 6) != 6) {
         }
         if (len == 0) {   //this is name info
            if (read(fd, &chan, 1) != 1) {
            }
            pi.channel = chan & 0x0F;
            dummyPacket[178] = chan & 0x10 ? 0 : 0x10;
            if (read(fd, &slen, 1) != 1) {
            }
            pi.name = (char*) malloc(slen + 1);
            if (read(fd, pi.name, slen) != slen) {
            }  
            pi.name[slen] = 0;
            addBssid(&cap);
         }
         else {
            pi.channel = 0;
            pi.pack = (Packet *) malloc(sizeof(Packet));
            cap.iv = pi.pack->buf = (unsigned char *) malloc(len - 6);
            pi.pack->len = len - 6;
            pi.pack->next = NULL;
            if (read(fd, pi.pack->buf, len - 6) != (len - 6)) {
            }
            addPacket(NULL, &cap, 1);
         }
      }
      close(fd);
   }
}

/***********************************************************

  format of saved data:

  For ksample packets:

  4 byte integer length of 802.11b data = n+6 (includes bssid)
  6 byte bssid
  n bytes of packet data
     3 byte IV
     n-3 bytes of data w/ 802.11b checksum at the end

  For sample packets

  4 byte integer length = 11
  6 byte bssid
  5 bytes of packet data
     3 byte IV
     1 dummy byte = 0
     1 byte = first byte of 802.11b data (the 0xAA byte)

  For AP name information

  4 byte integer length = 0
  6 byte bssid
  1 byte channel number
  1 byte length of name = n
  n byte AP name

************************************************************/

void
savePacketData                         (GtkButton       *button,
                                        gpointer         user_data)
{
   unsigned char first, dummy = 0;
   Packet *pkt;
   Sample *samp;
   BssidList *temp = head;
   int len, i;
   unsigned char uchar;
   char *fname = gtk_file_selection_get_filename(GTK_FILE_SELECTION(user_data));
   int fd = open(fname, O_WRONLY | O_CREAT, 0644);
   if (fd != -1) {
      for (; temp; temp = temp->next) {
         if (temp->name) {
            len = 0;
            write(fd, &len, sizeof(int));
            write(fd, temp->bssid, 6);
            uchar = temp->channel;
//            uchar |= temp->usingWep ? 0 : 0x10;
            write(fd, &uchar, 1);
            uchar = strlen(temp->name);
            write(fd, &uchar, 1);
            write(fd, temp->name, uchar);
         }
         if (temp->ap) {
            for (pkt = temp->ap->pkts; pkt; pkt = pkt->next) {
               len = pkt->len + 6;
               write(fd, &len, sizeof(int));
               write(fd, temp->bssid, 6);
               write(fd, pkt->buf, pkt->len);
            }
            len = 11;
            for (i = 0; i < 13; i++) {
               for (samp = temp->ap->samples[i]; samp; samp = samp->next) {
                  write(fd, &len, sizeof(int));
                  write(fd, temp->bssid, 6);
                  write(fd, samp->iv, 3);
                  write(fd, &dummy, 1);
                  first = samp->firstByte ^ 0xAA;
                  write(fd, &first, 1);
               }
            }
         }
      }
   }
   close(fd);
   if (quitting) {
      reallyQuit();
   }
}

void
on_savefile_cancel                     (GtkButton       *button,
                                        gpointer         user_data)
{
   if (quitting) {
      reallyQuit();
   }
}

void
on_stats_activate                      (GtkMenuItem     *menuitem,
                                        gpointer         user_data)
{
//fprintf(stderr, "stats\n");
}

void
on_delete_activate                     (GtkMenuItem     *menuitem,
                                        gpointer         user_data)
{
   int row, column, result;
//fprintf(stderr, "delete\n");

   result = gtk_clist_get_selection_info(theList, popupEvent->x, popupEvent->y, &row, &column);
   if (result) {
//fprintf(stderr, "row = %d, col = %d\n", row, column);
   }
   else {
//   fprintf(stderr, "not found\n");
   }
}


void
on_stop_activate                       (GtkMenuItem     *menuitem,
                                        gpointer         user_data)
{
//fprintf(stderr, "stop\n");
}


void
on_gps_activate                        (GtkMenuItem     *menuitem,
                                        gpointer         user_data)
{
   gtk_widget_show_all (create_GpsDialog());
}


void
on_GpsOk_clicked                       (GtkButton       *button,
                                        gpointer         user_data)
{

}

void
on_log_activate                        (GtkMenuItem     *menuitem,
                                        gpointer         user_data)
{
   logFileMenu = GTK_CHECK_MENU_ITEM(menuitem);
   if (logFileMenu->active) {
      gtk_widget_show_all (create_LogFile());
   }
   else if (logfile) {  //file is open, time to close
//      close(logfile);
//      logfile = -1;
   }
}

void
on_log_ok_clicked                      (GtkButton       *button,
                                        gpointer         user_data)
{
   //this will only take effect when user clicks start to begin a capture
   strcpy(dumpfile, gtk_file_selection_get_filename(GTK_FILE_SELECTION(user_data)));
}

void
on_log_cancel_button_clicked           (GtkButton       *button,
                                        gpointer         user_data)
{
   gtk_check_menu_item_set_active(logFileMenu, 0);
}

void
on_load_pcap_file_activate             (GtkMenuItem     *menuitem,
                                        gpointer         user_data)
{
   if (!doCapture) {
      gtk_widget_show_all (create_PcapFile());
   }
   else {  //can't load while capturing
      quick_message("Error", "Can't load while capture in progress");
   }

}

void
on_pcap_ok_clicked                     (GtkButton       *button,
                                        gpointer         user_data)
{
   CaptureArg *ca = (CaptureArg*) malloc(sizeof(CaptureArg));
   char *fname = gtk_file_selection_get_filename(GTK_FILE_SELECTION(user_data));
   ca->pcap = pcap_open_offline(fname, errbuf);
   if (ca->pcap) {
      switch (pcap_datalink(ca->pcap)) {
         case DLT_PRISM_HEADER:
            ca->offset = 144;
            break;
         default: //DLT_IEEE802_11
            ca->offset = 0;
      }
      ca->dump = NULL;
      oldscan = scan;
      scan = 0;
      doCapture = 1;
      readPcapFile = 1;
      pthread_create(&capThread, NULL, capture, ca);
   }

}

void
on_CardType_changed                    (GtkEditable     *editable,
                                        gpointer         user_data)
{
   char *ct = gtk_entry_get_text(GTK_ENTRY(editable));
   for (cardType = 0; cardType < NUM_CARDS; cardType++) {
      if (!strcmp(cards[cardType], ct)) break;
   }
   if (cardType == OTHER && realized) {
      //tell user that they must set monitor mode manually
      quick_message("Notice", "You must place your card into monitor mode\nmanually.  Channel scan may not be available");
   }
}


void
on_DeviceName_changed                  (GtkEditable     *editable,
                                        gpointer         user_data)
{
   strcpy(dev, gtk_entry_get_text(GTK_ENTRY(editable)));
}

void breadthChanged(void) {
   BssidList *node;
   for (node = head; node; node = node->next) {
      if (node->usingWep && !node->ap->cracked) {
         node->channelChanged = 1;
         sem_post(&(node->crackSem));
      }
   }
}

void
on_breadth40_changed                   (GtkEditable     *editable,
                                        gpointer         user_data)
{
   breadth40 = gtk_spin_button_get_value_as_int((GtkSpinButton*) editable);
   breadthChanged();
}


void
on_breadth128_changed                  (GtkEditable     *editable,
                                        gpointer         user_data)
{
   breadth128 = gtk_spin_button_get_value_as_int((GtkSpinButton*) editable);
   breadthChanged();
}


void
on_freq_realize                        (GtkWidget       *widget,
                                        gpointer         user_data)
{
   gtk_spin_button_set_value((GtkSpinButton*) widget, chan);
}


void
on_CardType_realize                    (GtkWidget       *widget,
                                        gpointer         user_data)
{
   gtk_entry_set_text (GTK_ENTRY (widget), cards[cardType]);
   realized = 1;
}


void
on_DeviceName_realize                  (GtkWidget       *widget,
                                        gpointer         user_data)
{
   gtk_entry_set_text(GTK_ENTRY(widget), dev);
}


void
on_breadth40_realize                   (GtkWidget       *widget,
                                        gpointer         user_data)
{
   gtk_spin_button_set_value((GtkSpinButton*) widget, breadth40);
}


void
on_breadth128_realize                  (GtkWidget       *widget,
                                        gpointer         user_data)
{
   gtk_spin_button_set_value((GtkSpinButton*) widget, breadth128);
}


