/* LinPopUp - A Linux enhanced port of Winpopup, running over Samba.
 * Copyright (c)1998-2000 Jean-Marc Jacquet <jm@littleigloo.org>  
 * Little Igloo Org  http://www.LittleIgloo.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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <gtk/gtk.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <errno.h>
#include <syslog.h>
#include "protos.h"

static GtkWidget *msg_file_sel;
static char SAVE_ALL_MSG;


void
update_main_statusbar (int message_number, char NEW)
{
  char buf[100];
  int f;

  for (f = 0; f < 4; f++)
    {
      gtk_statusbar_remove (GTK_STATUSBAR (main_statusbar[f]), main_context_id[f], main_message_id[f]);
      main_message_id[f] = gtk_statusbar_push (GTK_STATUSBAR (main_statusbar[f]), main_context_id[f], "");
    }

  gtk_statusbar_remove (GTK_STATUSBAR (main_statusbar[0]), main_context_id[0], main_message_id[0]);
  if (message_number)
    sprintf (buf, " Message #%i", message_number);
  else
    strcpy (buf, " No Message.");
  main_message_id[0] = gtk_statusbar_push (GTK_STATUSBAR (main_statusbar[0]), main_context_id[0], buf);

  if (NEW)
    {
      gtk_statusbar_remove (GTK_STATUSBAR (main_statusbar[1]), main_context_id[1], main_message_id[1]);
      main_message_id[1] = gtk_statusbar_push (GTK_STATUSBAR (main_statusbar[1]), main_context_id[1], " NEW ");
    }

  if (total_number_of_messages)
    {
      sprintf (buf, " Total Messages : %i", total_number_of_messages);
      gtk_statusbar_remove (GTK_STATUSBAR (main_statusbar[2]), main_context_id[2], main_message_id[2]);
      main_message_id[2] = gtk_statusbar_push (GTK_STATUSBAR (main_statusbar[2]), main_context_id[2], buf);
    }

  if (new_messages)
    {
      sprintf (buf, " New : %i", new_messages);
      gtk_statusbar_remove (GTK_STATUSBAR (main_statusbar[3]), main_context_id[3], main_message_id[3]);
      main_message_id[3] = gtk_statusbar_push (GTK_STATUSBAR (main_statusbar[3]), main_context_id[3], buf);
    }
}



/* -- read from data file message #message_number -- */
void
read_message (int message_number, struct linpopup_message *this_message)
{
  int file_handle;
  file_handle = open (DATA_FILE, O_RDONLY, DATA_PERM);
  /* lock it for reading .. */
  (void) lock_reg (file_handle, F_SETLKW, F_RDLCK, 0, SEEK_SET, 0);
  lseek (file_handle, (message_number - 1) * sizeof (*this_message), SEEK_SET);
  read (file_handle, this_message, sizeof (*this_message));
  /* unlock */
  (void) lock_reg (file_handle, F_SETLK, F_UNLCK, 0, SEEK_SET, 0);
  close (file_handle);
}



/* -- save to  data file message #message_number -- */
void
write_message (int message_number, struct linpopup_message *this_message)
{
  int file_handle;
  file_handle = open (DATA_FILE, O_WRONLY, DATA_PERM);
  /* lock it for writing .. */
  (void) lock_reg (file_handle, F_SETLKW, F_WRLCK, 0, SEEK_SET, 0);
  lseek (file_handle, (message_number - 1) * sizeof (*this_message), SEEK_SET);
  write (file_handle, this_message, sizeof (*this_message));
  /* unlock */
  (void) lock_reg (file_handle, F_SETLK, F_UNLCK, 0, SEEK_SET, 0);
  close (file_handle);
}



/* -- unset the is_new flag of message #message_number -- */
void
this_message_is_no_longer_new (int message_number)
{
  struct linpopup_message this_message;
  read_message (message_number, &this_message);
  this_message.is_new = FALSE;
  write_message (message_number, &this_message);
  new_messages--;
}



/* -- show_no_message -- */
void
show_no_message (void)
{
  char buf[1024];
  char *ptr;
  static GdkColor text_fg = { 0, 0x8000, 0x8000, 0x8000 };
  GdkColormap *colormap;

  clear_text (main_text);
  colormap = gtk_widget_get_colormap (window);
  gdk_color_alloc (colormap, &text_fg);


  many_version_infos (buf);
  ptr = strchr (buf, '\n');

  gtk_text_freeze (GTK_TEXT (main_text));
  gtk_text_insert (GTK_TEXT (main_text), NULL, &text_fg, NULL, "Distributed under the terms of the GNU GPL.", -1);
  //gtk_text_insert (GTK_TEXT (main_text), NULL, &text_fg, NULL, ptr + 1, -1);

  *ptr = 0;
  gtk_label_set (GTK_LABEL (main_label_from), buf);

  if (samba_installed)
    sprintf (buf, "\n%s installed.", samba_version);

  gtk_text_insert (GTK_TEXT (main_text), NULL, &text_fg, NULL, buf, -1);

  if (strstr (samba_version, "ion 1.") != NULL)
    gtk_text_insert (GTK_TEXT (main_text), NULL, &text_fg, NULL, "\n\nWARNING :\nDue to a security hole found in Samba message command you may\ninstall Samba version >= 2.0 Beta 2 when using LinPopUp.", -1);


  gtk_text_thaw (GTK_TEXT (main_text));

  current_showed_message = 0;
  update_main_statusbar (0, FALSE);
}



/* -- show message #message_number -- */
void
show_message (int message_number)
{
  struct linpopup_message this_message;
  char buf[250];
  char time_buf[30];
  struct tm *timeptr;
  static GdkColor header_fg = { 0, 0x8000, 0x0000, 0x0000 };
  GdkColormap *colormap = gtk_widget_get_colormap (window);

  gdk_color_alloc (colormap, &header_fg);

  read_message (message_number, &this_message);

  timeptr = localtime (&this_message.time);
  strftime (time_buf, sizeof (time_buf), "%a %d %b %Y  %X", timeptr);
  sprintf (buf, "Received from %s at %s, %s", this_message.sender, this_message.host, time_buf);
  gtk_label_set (GTK_LABEL (main_label_from), buf);

  clear_text (main_text);
  realize_text (main_text, this_message.text);

  if (this_message.is_new)
    {
      update_main_statusbar (message_number, TRUE);
      this_message_is_no_longer_new (message_number);
    }
  else
    update_main_statusbar (message_number, FALSE);

  deiconify_window (window);
  current_showed_message = message_number;
  strcpy (user_rc.guess_adr, this_message.host);
}



/* -- show next message -- */
void
show_next_message (gpointer * data)
{
  if (!total_number_of_messages)
    {
      show_no_message ();
      return;
    }

  if (current_showed_message == total_number_of_messages)
    user_rc.wrap_to_first ? show_message (1) : show_message (total_number_of_messages);
  else
    show_message (current_showed_message + 1);

}



/* -- show previous message -- */
void
show_previous_message (gpointer * data)
{
  if (!total_number_of_messages)
    {
      show_no_message ();
      return;
    }

  if (current_showed_message == 1)
    user_rc.wrap_to_first ? show_message (total_number_of_messages) : show_message (1);
  else
    show_message (current_showed_message - 1);
}



/* -- count  messages in data file -- */
int
count_total_number_of_messages (void)
{
  struct stat data_file;
  int data_file_len;

  if (access (DATA_FILE, F_OK))
    return 0;

  stat (DATA_FILE, &data_file);
  data_file_len = ((off_t) data_file.st_size / sizeof (struct linpopup_message));
  return data_file_len;
}



/* -- save message #message_number in a human readable form -- */
static void
save_message (FILE * out, int message_number)
{
  struct linpopup_message this_message;
  char time_buf[30];
  struct tm *timeptr;

  read_message (message_number, &this_message);
  timeptr = localtime (&this_message.time);
  strftime (time_buf, sizeof (time_buf), "%a %d %b %Y  %X", timeptr);
  fprintf (out, "FROM : %s ON %s\nTO   : %s\nDATE : %s\n---\n\n%s\n=== END OF MESSAGE ===\n\n\n\n\n\n", strupr (this_message.sender), strupr (this_message.host), strupr (this_message.to), strupr (time_buf), this_message.text);
}



/* == Message save fileselector  ==  */
static void
message_save_ok (GtkWidget * widget, GtkFileSelection * file_sel)
{
  char *filename;
  FILE *out;

  filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION (file_sel));
  if (filename != NULL)
    {
      if (!access (filename, F_OK))
	{
	  char buf[1024];
	  sprintf (buf, "\n %s already exists\nErase it anyway ?", strrchr (filename, '/') + 1);
	  if (alert_dialog ("Confirmation box", buf, "Yes", "No", 2) == 2)
	    return;
	}
      if ((out = fopen (filename, "w")))
	{
	  if (SAVE_ALL_MSG)
	    {
	      int f;
	      for (f = 1; f <= total_number_of_messages; f++)
		{
		  fprintf (out, "LINPOPUP MESSAGE #%i \n", f);
		  save_message (out, f);
		}
	    }
	  else
	    {
	      fprintf (out, "LINPOPUP MESSAGE\n");
	      save_message (out, current_showed_message);
	    }
	  fclose (out);
	}
      else
	{
	  char buf[1024];
	  sprintf (buf, "\n Can not write %s !", strrchr (filename, '/') + 1);
	  alert_ok (" Error", buf, "Ok");
	  return;
	}
    }
  gtk_widget_destroy (GTK_WIDGET (file_sel));
  msg_file_sel = NULL;
}



static void
message_save_cancel (GtkWidget * widget, GtkFileSelection * file_sel)
{
  gtk_widget_destroy (GTK_WIDGET (file_sel));
  msg_file_sel = NULL;
}



void
message_save (GtkWidget * widget, int data)
{
  if (!total_number_of_messages)
    return;

  if (msg_file_sel == NULL)
    {
      SAVE_ALL_MSG = (int) data;
      msg_file_sel = gtk_file_selection_new ("Save Message ...");
      gtk_signal_connect (GTK_OBJECT (msg_file_sel), "destroy", GTK_SIGNAL_FUNC (gtk_widget_destroy), msg_file_sel);
      gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (msg_file_sel)->ok_button), "clicked", GTK_SIGNAL_FUNC (message_save_ok), msg_file_sel);
      gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (msg_file_sel)->cancel_button), "clicked", GTK_SIGNAL_FUNC (message_save_cancel), msg_file_sel);
    }

  if (GTK_WIDGET_VISIBLE (msg_file_sel))
    return;
  else
    gtk_widget_show (msg_file_sel);
}



/* -- delete message current_showed message from  data file -- */
void
delete_showed_message (void)
{
  struct linpopup_message this_message;
  char *temp_filename;
  int file_handle_temp, file_handle_data;
  int f;


  if (!total_number_of_messages)
    {
      show_no_message ();
      return;
    }

  temp_filename = tmpnam (NULL);
  file_handle_temp = open (temp_filename, O_RDWR | O_CREAT, DATA_PERM);
  file_handle_data = open (DATA_FILE, O_RDONLY, DATA_PERM);
  /* lock it for reading .. */
  (void) lock_reg (file_handle_data, F_SETLKW, F_RDLCK, 0, SEEK_SET, 0);


  lseek (file_handle_data, 0L, SEEK_SET);
  for (f = 1; f <= total_number_of_messages; f++)
    {
      read (file_handle_data, &this_message, sizeof (this_message));
      write (file_handle_temp, &this_message, sizeof (this_message));
    }

  /* unlock */
  (void) lock_reg (file_handle_data, F_SETLK, F_UNLCK, 0, SEEK_SET, 0);
  close (file_handle_data);
  close (file_handle_temp);

  file_handle_temp = open (temp_filename, O_RDONLY, DATA_PERM);
  file_handle_data = open (DATA_FILE, O_RDWR | O_TRUNC, DATA_PERM);
  /* lock it for writing .. */
  (void) lock_reg (file_handle_data, F_SETLKW, F_WRLCK, 0, SEEK_SET, 0);

  for (f = 1; f <= total_number_of_messages; f++)
    {

      read (file_handle_temp, &this_message, sizeof (this_message));
      if (f != current_showed_message)
	write (file_handle_data, &this_message, sizeof (this_message));
      else if (this_message.is_new == TRUE)
	new_messages--;
    }

  /* unlock */
  (void) lock_reg (file_handle_data, F_SETLK, F_UNLCK, 0, SEEK_SET, 0);
  close (file_handle_data);
  close (file_handle_temp);

  unlink (temp_filename);

  total_number_of_messages = count_total_number_of_messages ();
  current_showed_message--;
  show_next_message (NULL);

  if (!total_number_of_messages && user_rc.auto_iconify)
    iconify_window (NULL, NULL);
}



/* -- delete ALL messages in the data file -- */
void
delete_all_messages (void)
{
  int file_handle_data;

  if (!total_number_of_messages)
    return;
  if (alert_dialog ("Confirm for deletion", "\n Do you really want\ndelete ALL messages ?", "Yes", "No", 2) == 2)
    return;
  file_handle_data = open (DATA_FILE, O_RDWR | O_TRUNC, DATA_PERM);
  close (file_handle_data);
  total_number_of_messages = count_total_number_of_messages ();
  new_messages = 0;
  show_no_message ();
}



/* -- return the numb of new message in data file -- */
int
are_there_new_messages_in_data_file (void)
{
  struct linpopup_message this_message;
  int file_handle;
  int compt;

  first_new_message = 0;
  new_messages = 0;

  if (access (DATA_FILE, F_OK))
    return 0;

  if ((file_handle = open (DATA_FILE, O_RDONLY, DATA_PERM)) == -1)
    strerror (errno);
  /* lock it for reading .. */
  (void) lock_reg (file_handle, F_SETLKW, F_RDLCK, 0, SEEK_SET, 0);

  for (compt = 1; compt <= total_number_of_messages; compt++)
    {
      read (file_handle, &this_message, sizeof (this_message));
      if (this_message.is_new == TRUE)
	{
	  new_messages++;
	  if (first_new_message == 0)
	    first_new_message = compt;
	}
    }

  /* unlock */
  (void) lock_reg (file_handle, F_SETLK, F_UNLCK, 0, SEEK_SET, 0);
  close (file_handle);
  return new_messages;
}



/* -- New message regular check  -- */
gint check_for_new_message (gpointer data)
{
  if (processing_new_message)
    return TRUE;

  total_number_of_messages = count_total_number_of_messages ();
  if (!are_there_new_messages_in_data_file ())
    return TRUE;

  processing_new_message = TRUE;
  show_message (first_new_message);
  processing_new_message = FALSE;

  return TRUE;
}





/* -- HERE IS THE FUNCTION INVOKED WHEN LAUNCHED BY SAMBA -- */
void
trap_message (int argc, char *argv[])
{
  FILE *in;
  int file_handle;
  int i;
  struct stat stat_buf;
  struct linpopup_message this_message;

  vital_check_for_read_access (argv[3]);
  memset (&this_message, 0, sizeof (this_message));

  stat (argv[3], &stat_buf);
  this_message.time = stat_buf.st_atime;
  this_message.is_new = TRUE;
  strncpy (this_message.sender, argv[1], sizeof (this_message.sender) - 1);
  strncpy (this_message.host, argv[2], sizeof (this_message.host) - 1);
  if (argc > 4)
    strncpy (this_message.to, argv[4], sizeof (this_message.to) - 1);

  in = fopen (argv[3], "r");
  i = fread (this_message.text, 1, sizeof (this_message.text), in);
  fclose (in);
  this_message.text[i] = 0;


  if (access (DATA_FILE, F_OK))
    file_handle = open (DATA_FILE, O_WRONLY | O_CREAT, DATA_PERM);
  else
    file_handle = open (DATA_FILE, O_WRONLY | O_APPEND, DATA_PERM);

  /* lock it for writing .. */
  (void) lock_reg (file_handle, F_SETLKW, F_WRLCK, 0, SEEK_SET, 0);

  write (file_handle, &this_message, sizeof (this_message));

  /* unlock */
  (void) lock_reg (file_handle, F_SETLK, F_UNLCK, 0, SEEK_SET, 0);

  close (file_handle);

  exit (0);
}

/* EOF */
