/* GKrellM mailwatch plugin
|  Copyright (C) 1999-2001 Sjoerd Simons
|
|  Author:  Sjoerd Simons Sjoerd@luon.net
|
|  This program is free software which I release under the GNU General Public
|  License. You may redistribute and/or modify this program under the terms
|  of that 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.
|
|  To get a copy of the GNU General Puplic License,  write to the
|  Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|  02111-1307, USA.
*/

#include <dirent.h>
#include <sys/types.h>
#include <time.h>
#include <utime.h>
#include <string.h>
#include <libgen.h>
#include <glob.h>

#include "mailwatch.h"
/* Some global stuff */

static Mailpanel *mailpanels;
static GtkWidget *panelbox;

static gint toggles = 0;
static gint ctoggles;
static gint animation_steps = 6;
static gint canimation_steps;

static gint style_id;

static gint check_mh(Mailbox * mbox);
static gint check_sylpheed_maildir(Mailbox * mbox);
static gint check_maildir(Mailbox * mbox);

static GkrellmMonitor plugin_mon;

/*----  Code adapted from Gkrellm's mail.c and other sources ---- */
static gint
is_From_line(Mailbox * mbox, gchar * buf) {
  gchar sender[512];
  gint dayofmonth = 0;

  if (strncmp(buf, "From ", 5))
    return FALSE;

  /* In case sending address missing, look for a day of month
   * |  number in field 3 or 4 (0 based).
   */
  sender[0] = '\0';
  if (sscanf(buf, "%*s %*s %*s %d", &dayofmonth) != 1) {
    if (sscanf(buf, "%*s %s %*s %*s %d", sender, &dayofmonth) != 2)
      return FALSE;
  }
  if (dayofmonth < 1 || dayofmonth > 31)
    return FALSE;
  if (strcmp(sender, "MAILER-DAEMON") == 0)
    mbox->is_internal = TRUE;
  return TRUE;
}

static gint
is_mh_mailbox(Mailbox * mbox) {
  struct stat s;
  int ret;

  gchar *path = g_strdup_printf("%s/.mh_sequences", mbox->path);

  ret = stat(path, &s);
  free(path);

  return (ret ? FALSE : TRUE);
}

static gint
is_sylpheed_maildir(Mailbox * mbox) {
  struct stat s;
  int ret;
  gchar *path = g_strdup_printf("%s/.sylpheed_mark", mbox->path);

  ret = stat(path, &s);
  g_free(path);

  return (ret ? FALSE : TRUE);
}

static gint
check_dir(Mailbox * mbox) {
  if (is_mh_mailbox(mbox))
    return check_mh(mbox);
  else if (is_sylpheed_maildir(mbox))
    return check_sylpheed_maildir(mbox);
  else
    return check_maildir(mbox);
}

static gint
total_mh(char *path) {
  DIR *mhdir = opendir(path);
  struct dirent *de = NULL;
  int total = 0;

  if (!mhdir)
    return 0;

  while ((de = readdir(mhdir))) {
    int n;
    char c;
    int tokens = sscanf(de->d_name, "%d%1[^0-9]", &n, &c);

    if (tokens == 1)
      total++;
  }

  closedir(mhdir);

  return total;
}

static gint
check_sylpheed_maildir(Mailbox * mbox) {
  FILE *f;
  gint flags;
  gchar *file = g_strdup_printf("%s/.sylpheed_mark", mbox->path);
  gint version;
  gint num;
  gint files_in_dir = total_mh(mbox->path);
  gint files_seen = 0;

  mbox->old_new_mail_count = mbox->new_mail_count;
  mbox->new_mail_count = 0;

  if (toggles & SHOW_TOTAL)
    mbox->mail_count = files_in_dir;
  else
    mbox->mail_count = 0;

  if ((f = fopen(file, "rb")) == NULL)
    return FALSE;

  if (fread(&version, sizeof(version), 1, f) != 1 || version != 2) {
    fclose(f);
    return FALSE;
  }

  while (fread(&num, sizeof(num), 1, f) == 1) {
    if (fread(&flags, sizeof(flags), 1, f) != 1)
      break;

    if (flags & 1)
      mbox->new_mail_count++;
    else if (toggles & O_IS_NEW && flags & 2)
      mbox->new_mail_count++;

    files_seen++;
  }

  if (files_seen < files_in_dir)
    mbox->new_mail_count += (files_in_dir - files_seen);

  fclose(f);

  return TRUE;
}

static gint
check_mh(Mailbox * mbox) {
  FILE *f = 0;
  gchar buf[1024], *ptr = buf;
  gint num = 0, incr = 0;
  gint first = 0, last = 0;
  gint found = FALSE;
  gchar *path = NULL;

  mbox->old_new_mail_count = mbox->new_mail_count;
  mbox->new_mail_count = 0;
  mbox->mail_count = 0;

  /* Check the total mail */
  if (toggles & SHOW_TOTAL)
    mbox->mail_count = total_mh(mbox->path);

  path = g_strdup_printf("%s/.mh_sequences", mbox->path);
  f = fopen(path, "r");
  free(path);

  if (f == NULL)
    return TRUE;

  /* Check the unseen mail */
  while (fgets(buf, sizeof(buf), f), !feof(f))
    if (!strncmp(buf, "unseen", sizeof("unseen") - 1)) {
      found = 1;
      break;
    }

  fclose(f);

  if (!found)
    return TRUE;

  /* OK now parse the form 'unseen: <num>[-<num>] [<num>[-<num>] ...]' */
  num = sscanf(ptr, "unseen: %d%n", &first, &incr);

  if (num != 1)
    return TRUE;

  while (num == 1) {
    /* See if there's a range */
    ptr += incr;
    num = sscanf(ptr, "-%d%n", &last, &incr);

    if (num == 1) {
      ptr += incr;
      mbox->new_mail_count += last - first + 1;
    } else {
      mbox->new_mail_count++;
    }

    num = sscanf(ptr, " %d%n", &first, &incr);
  }

  return TRUE;
}

/* According to gkrellm's mail.c ;0 
 | A maildir has new, cur, and tmp subdirectories.  Any file in new
 |  or cur that does not begin with a '.' is a mail message.  It is
 |  suggested that messages begin with the output of time() (9 digits)
 |  but while mutt and qmail use this standard, procmail does not.
 |  maildir(5) says:
 |      It is a good idea for readers to skip all filenames in
 |      new and cur starting with a dot.  Other than this,
 |      readers should not attempt to parse filenames.
 |  So check_maildir() simply looks for files in new and cur.
 |  See also http://cr.yp.to/proto/maildir.html
*/
static gint
check_maildir(Mailbox * mbox) {
  gchar *buf;
  struct dirent *entry;
  DIR *folder_new, *folder_cur;

  buf = g_strdup_printf("%s/new", mbox->path);
  if (!(folder_new = opendir(buf))) {
    g_free(buf);
    return FALSE;
  }
  g_free(buf);

  buf = g_strdup_printf("%s/cur", mbox->path);
  if (!(folder_cur = opendir(buf))) {
    closedir(folder_new);
    g_free(buf);
    return FALSE;
  }
  g_free(buf);

  mbox->mail_count = 0;
  mbox->old_mail_count = 0;
  mbox->old_new_mail_count = mbox->new_mail_count;
  mbox->new_mail_count = 0;

  while ((entry = readdir(folder_new)) != NULL) {
    if (*entry->d_name != '.' && entry->d_ino > 0) {
      mbox->new_mail_count++;
      mbox->mail_count++;
    }
  }
  closedir(folder_new);

  if (toggles & SHOW_TOTAL || toggles & O_IS_NEW)
    while ((entry = readdir(folder_cur)) != NULL) {
      gchar *tmp = NULL, *tmp1 = NULL;

      if (*entry->d_name != '.' && entry->d_ino > 0) {
        mbox->mail_count++;
        if (toggles & O_IS_NEW) {
          tmp = strchr(entry->d_name, ':');
          tmp1 = tmp != NULL ? strchr(tmp, 'S') : NULL;
          if (tmp1 == NULL) {
            mbox->new_mail_count++;
          }
        }
      }
    }
  closedir(folder_cur);

  mbox->old_mail_count = mbox->mail_count - mbox->new_mail_count;

  return TRUE;
}

/* Taken from gkrellm mail.c */
static gboolean
status_is_old(gchar * buf) {
  gchar c;

  if (buf[0] != 'S' && buf[0] != 'X')
    return FALSE;

/* Standard mail clients */
  if (!strncmp(buf, "Status:", 7) && (strchr(buf, 'R')
                                      || (!(toggles & O_IS_NEW)
                                          && strchr(buf, 'O'))))
    return TRUE;

/* Netscape */
  if (!strncmp(buf, "X-Mozilla-Status:", 17) && (strlen(buf) > 21)) {
    c = buf[21];
    if (c < '8')                /* Not deleted */
      c -= '0';
    if (c >= '8' || (c & 0x1))
      return TRUE;
  }

/* Evolution */
  if (!strncmp(buf, "X-Evolution:", 12) && (strlen(buf) > 24)) {
    if (strncmp(buf + 24, "1", 1) || !strncmp(buf + 24, "3", 1))
      return TRUE;
  }
  return FALSE;
}


static gint
check_mailbox(Mailbox * mbox, struct stat *s) {
  FILE *f;
  gchar buf[1024];
  struct utimbuf ut;
  gint n;

  /* If the mailboxes have been modified since last check, count
   * |  the new/total messages.
   */
  if (s->st_mtime != mbox->last_mtime || s->st_size != mbox->last_size) {
    if ((f = fopen(mbox->path, "r")) == NULL)
      return FALSE;
    mbox->mail_count = 0;
    mbox->old_mail_count = 0;
    for (;;) {
      long msg_length = 0;
      gint is_new = -1;

      /* Look for From line, marking beginning of message
       * This skips over eventual garbage.
       */
      while (fgets(buf, sizeof(buf), f)) {
        if (is_From_line(mbox, buf)) {
          mbox->mail_count += 1;
          is_new = 1;
          break;
        }
      }
      /* Parse headers */
      while (fgets(buf, sizeof(buf), f)) {
        if (buf[0] == '\n') {
          mbox->is_internal = FALSE;
          break;
        } else if (status_is_old(buf)) {
          is_new = 0;
        } else if (sscanf(buf, "Content-Length: %ld\n", &msg_length) == 1) {
        } else if (mbox->is_internal) {
          if (strncmp(buf, "From: Mail System Internal Data", 31) == 0) {
            mbox->mail_count -= 1;
            mbox->is_internal = FALSE;
            break;
          }
        }
      }
      if (is_new == 0) {
        mbox->old_mail_count += 1;
      }
      if (feof(f) || ferror(f)) {
        break;
      } else if (msg_length > 0) {
        fseek(f, msg_length, SEEK_CUR);
      }
    }
    fclose(f);

    /* Restore the mbox stat times for other mail checking programs and
     * |  so the (st_atime > st_mtime) animation override below will work.
     */

    ut.actime = s->st_atime;
    ut.modtime = s->st_mtime;
    utime(mbox->path, &ut);

    mbox->last_mtime = s->st_mtime;
    mbox->last_size = s->st_size;

    n = mbox->mail_count - mbox->old_mail_count;
    mbox->old_new_mail_count = mbox->new_mail_count;
    mbox->new_mail_count = n;
  }
  return TRUE;
}



/*----> my own code ;) <----  */


void
panel_entered(GtkWidget * widget, GdkEventCrossing * event, Mailpanel * p) {
  p->update_tooltip = FALSE;
}

void
panel_left(GtkWidget * widget, GdkEventCrossing * event, Mailpanel * p) {
  p->update_tooltip = TRUE;
}


static gboolean
plug_expose_event(GtkWidget * widget, GdkEventExpose * event) {
  Mailpanel *p;

  for (p = mailpanels; p; p = p->next)
    if (widget == p->panel->drawing_area) {
      gdk_draw_pixmap(widget->window,
                      widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
                      p->panel->pixmap,
                      event->area.x, event->area.y,
                      event->area.x, event->area.y,
                      event->area.width, event->area.height);
      return FALSE;
    }

  return FALSE;
}

static gint
button_release(GtkWidget * widget, GdkEventButton * ev, Mailpanel * panel) {
  gchar *command;

  if (panel->command == NULL)
    return FALSE;
  command = malloc((strlen(panel->command) + 4) * sizeof(char));
  strcpy(command, panel->command);
  system(strcat(command, " &"));
  free(command);
  return FALSE;
}

static gint
findpath(gconstpointer a, gconstpointer b) {
  return strcmp(((Mailpath *) a)->path, (char *) b);
}

static GList *
add_mailbox(GList * list, gchar * path) {
  Mailbox *mbox;

  if (g_list_find_custom(list, path, findpath) != NULL)
    return list;

  mbox = g_new0(Mailbox, 1);
  mbox->path = strdup(path);
  return g_list_prepend(list, mbox);
}

static void
update_plugin(void) {
  gint old_nmail, nmail, mail, new, old_new, total;
  gchar *buf = NULL;
  gchar *tiptext, *ntiptext;
  GList *path_list, *mbox_list, *mnext;
  Mailbox *mailbox;
  Mailpanel *p;
  Mailpath *mpath;
  gchar *bname;
  struct stat s;
  GkrellmTextstyle *ts;
  glob_t pglob;
  gchar *path;
  int i;

  if (mailpanels == NULL)
    return;

  if (GK.second_tick)
    for (p = mailpanels; p; p = p->next) {
      old_nmail = 0;
      nmail = 0;
      mail = 0;
      tiptext = NULL;

      p->ticks_left--;
      if (p->ticks_left > 0)
        continue;
      p->ticks_left = p->ticks;

      for (path_list = p->mailpath_list; path_list;
           path_list = path_list->next) {
        mpath = (Mailpath *) path_list->data;

        if (mpath->path[0] == '~') {
          path = g_strjoin(NULL, g_get_home_dir(), mpath->path + 1, NULL);
        } else {
          path = g_strdup(mpath->path);
        }

        if (glob(path, 0, NULL, &pglob) != 0) {
          g_free(path);
          continue;
        }
        g_free(path);

        for (i = 0; i < pglob.gl_pathc; i++) {
          mpath->mailbox_list =
            add_mailbox(mpath->mailbox_list, pglob.gl_pathv[i]);
        }

        globfree(&pglob);

        for (mbox_list = mpath->mailbox_list; mbox_list; mbox_list = mnext) {
          mnext = mbox_list->next;

          mailbox = (Mailbox *) mbox_list->data;
          if (stat(mailbox->path, &s) < 0) {
            free(mailbox->path);
            mpath->mailbox_list = g_list_remove(mpath->mailbox_list, mailbox);
            continue;
          }

          if ((S_ISDIR(s.st_mode) && check_dir(mailbox))
              || (!S_ISDIR(s.st_mode) && check_mailbox(mailbox, &s))) {

            old_new = mailbox->old_new_mail_count;
            new = mailbox->new_mail_count;
            if (old_new < 0)
              old_new = new;
            total = mailbox->mail_count;

            bname = g_path_get_basename(mailbox->path);
            if (toggles & SHOW_TOTAL) {
              buf = g_strdup_printf("%s: %d/%d", bname, new, total);
            } else {
              buf = g_strdup_printf("%s: %d", bname, new);
            }
            g_free(bname);

            if (tiptext == NULL) {
              tiptext = strdup(buf);
            } else {
              ntiptext = g_strdup_printf("%s\n%s", tiptext, buf);
              free(tiptext);
              tiptext = ntiptext;
            }
            free(buf);
            old_nmail += old_new;
            nmail += new;
            mail += total;
          }
        }
      }
      if (old_nmail < nmail) {
        p->animation_step = animation_steps;
      } else if (old_nmail > nmail) {
        /* mail is being read, stop blinking */
        p->animation_step = 0;
      }

      if (!(p->animation_step & 0x1)) {
        if (toggles & SHOW_TOTAL) {
          buf = g_strdup_printf("%d/%d", nmail, mail);
        } else {
          buf = g_strdup_printf("%d", nmail);
        }
      } else {
        buf = g_strdup_printf(" ");
      }

      if (p->update_tooltip)
        gtk_tooltips_set_tip(p->tip, p->panel->drawing_area, tiptext, NULL);

      /* we don't need tiptext anymore */
      free(tiptext);

      if (toggles & SHOW_TIPS) {
        gtk_tooltips_enable(p->tip);
      } else {
        gtk_tooltips_disable(p->tip);
      }

      if ((toggles & HLIGHT_NEW) && (nmail > 0)) {
        ts = gkrellm_meter_alt_textstyle(style_id);
      } else {
        ts = gkrellm_meter_textstyle(style_id);
      }

      p->nametext->text_style = p->statstext->text_style = *ts;

      gkrellm_draw_decal_text(p->panel, p->nametext, p->name,
                              nmail + mail + toggles);

      p->statstext->x_off =
        gkrellm_chart_width() -
        gdk_string_width(p->panel->textstyle->font,
                         buf) -
        2 * gkrellm_get_style_margins(p->panel->style)->left;

      gkrellm_draw_decal_text(p->panel, p->statstext, buf,
                              nmail + mail + toggles + p->animation_step);
      /* change the anmiation step */
      if (p->animation_step > 0)
        p->animation_step--;
      free(buf);
      /* nmail+mail+toggles should make almost sure, that we always draw
       * the decal string when something changes... */
      gkrellm_draw_panel_layers(p->panel);
    }
}

static void
display_panel(Mailpanel * p, gint first_create) {
  GkrellmPiximage *bg_image;
  GkrellmStyle *panel_style;

  panel_style = gkrellm_meter_style(style_id);
  bg_image = gkrellm_bg_meter_piximage(style_id);

  if (first_create)
    p->panel = gkrellm_panel_new0();
  else
    gkrellm_destroy_decal_list(p->panel);

  p->panel->textstyle = gkrellm_meter_textstyle(style_id);
  p->nametext = gkrellm_create_decal_text(p->panel, "0", p->panel->textstyle,
                                          panel_style, -1, -1, -1);
  p->statstext =
    gkrellm_create_decal_text(p->panel, "0", p->panel->textstyle,
                              panel_style, -1, -1, -1);
  panel_style->label_position = 10;
  gkrellm_panel_configure(p->panel, NULL, panel_style);
  gkrellm_panel_create(panelbox, &plugin_mon, p->panel);
  if (first_create) {
    gtk_signal_connect(GTK_OBJECT(p->panel->drawing_area), "expose_event",
                       (GtkSignalFunc) plug_expose_event, NULL);
    gtk_signal_connect(GTK_OBJECT(p->panel->drawing_area),
                       "button_release_event",
                       (GtkSignalFunc) button_release, p);
    gtk_signal_connect(GTK_OBJECT(p->panel->drawing_area),
                       "enter-notify-event",
                       (GtkSignalFunc) panel_entered, p);
    gtk_signal_connect(GTK_OBJECT(p->panel->drawing_area),
                       "leave-notify-event", (GtkSignalFunc) panel_left, p);
  }
  gkrellm_draw_panel_layers(p->panel);
  p->tip = gtk_tooltips_new();
  p->update_tooltip = TRUE;
  if (toggles & SHOW_TIPS) {
    gtk_tooltips_enable(p->tip);
  } else {
    gtk_tooltips_disable(p->tip);
  }
}

static Mailpanel *
create_mailpanel(gchar name[]) {
  Mailpanel *p, *q;

  q = NULL;
  if (mailpanels == NULL) {
    mailpanels = g_new0(Mailpanel, 1);
    mailpanels->name = strdup(name);
    mailpanels->command = NULL;
    mailpanels->next = NULL;
    mailpanels->animation_step = 0;
    mailpanels->ticks = 1;
    mailpanels->ticks_left = 1;
    return mailpanels;
  }

  for (p = mailpanels; p && strcmp(p->name, name); p = p->next)
    q = p;
  if (p != NULL) {
    return NULL;
  }
  p = g_new0(Mailpanel, 1);
  p->name = strdup(name);
  p->command = NULL;
  p->next = NULL;
  p->animation_step = 0;
  p->ticks = 1;
  p->ticks_left = 1;
  q->next = p;
  return p;
}

static gint
change_command(gchar name[], gchar * command) {
  Mailpanel *p;

  for (p = mailpanels; p != NULL && strcmp(p->name, name); p = p->next);
  if (p == NULL)
    return FALSE;

  if (p->command != NULL)
    free(p->command);

  if (command == NULL)
    p->command = NULL;
  else
    p->command = strdup(command);

  return TRUE;
}

static gint
change_ticks(gchar name[], gint ticks) {
  Mailpanel *p;

  for (p = mailpanels; p != NULL && strcmp(p->name, name); p = p->next);
  if (p == NULL)
    return FALSE;

  p->ticks = ticks;
  p->ticks_left = 1;
  return TRUE;
}

static gint
add_mailpath(gchar name[], gchar path[]) {
  Mailpath *mpath;
  Mailpanel *p;
  GList *list;

  for (p = mailpanels; p && strcmp(p->name, name); p = p->next);
  if (p == NULL)
    return FALSE;

  for (list = p->mailpath_list; list; list = list->next)
    /* if mailpath already exist, don't add it */
    if (!strcmp(((Mailpath *) list->data)->path, path))
      return TRUE;
  mpath = g_new0(Mailpath, 1);
  mpath->path = strdup(path);
  p->mailpath_list = g_list_append(p->mailpath_list, mpath);
  return TRUE;
}

static void
del_mailboxlist(GList * list) {
  GList *list2;

  for (list2 = list; list2; list2 = list2->next) {
    free(((Mailbox *) list->data)->path);
    free(list->data);
  }
  g_list_free(list);
}

static gint
del_mailpathlist(gchar name[]) {
  GList *list, *list2;
  Mailpanel *p;

  for (p = mailpanels; p && strcmp(p->name, name); p = p->next);

  if (p == NULL)
    return FALSE;

  list2 = p->mailpath_list;
  p->mailpath_list = NULL;
  for (list = list2; list; list = list->next) {
    del_mailboxlist(((Mailpath *) list->data)->mailbox_list);
    free(((Mailpath *) list->data)->path);
    free(list->data);
  }
  g_list_free(list2);
  return TRUE;
}

static void
del_mailpanel(gchar name[]) {
  Mailpanel *p, *q;

  q = NULL;
  for (p = mailpanels; p != NULL && strcmp(p->name, name); p = p->next)
    q = p;

  if (p == NULL)
    return;

  del_mailpathlist(name);
  if (q == NULL)
    mailpanels = p->next;
  else
    q->next = p->next;
/*   mailpanel is now out of the mailpanel list,   
     now to erase the panel and stuff    	   */

  free(p->name);
  if (p->command != NULL)
    free(p->command);
  gkrellm_destroy_decal_list(p->panel);
  gkrellm_panel_destroy(p->panel);
  free(p);
}

static void
create_plugin(GtkWidget * vbox, gint first_create) {
  Mailpanel *p;

  if (panelbox == NULL) {
    panelbox = gtk_vbox_new(FALSE, 0);
    gtk_container_add(GTK_CONTAINER(vbox), panelbox);
    gtk_widget_show(panelbox);
  }

  for (p = mailpanels; p; p = p->next)
    display_panel(p, first_create);
}

/*---> Configure stuff <---*/

static Configtab *ctabs = NULL;

static Configtab
  * create_configtab(GtkWidget * tabs, gchar * name, gchar * command,
                     gint pos, gint mailpanel, gint ticks);


static void
clist_mailbox_selected(GtkWidget * clist, gint row, gint column,
                       GdkEventButton * bevent, gpointer data) {
  ((Configtab *) data)->row = row;
}

static void
clist_enter(GtkWidget * button, Configtab * data) {
  gchar *buf[2];
  Configtab *i, *new;
  gint pos = 0;

  buf[0] = (gchar *) gtk_entry_get_text(GTK_ENTRY(data->entry));
  buf[1] = NULL;
  if (!strlen(buf[0])) {
    gkrellm_message_window(_("GGkrellmKrellm mailwatch error"),
                           _("Won't make a nameless entry.."), NULL);
    return;
  }
  if (data->is_mailpanel) {
    data->mailbox_list = g_list_append(data->mailbox_list, strdup(buf[0]));
    if (data->is_modified != NEW) {
      data->is_modified = MODIFIED;
    }
  } else {
    for (i = ctabs; i != NULL; i = i->next) {
      if (!strcmp(i->name, buf[0]) && i->is_modified != DELETED) {
        gkrellm_message_window(_("Gkrellm mailwatch Error"),
                               _
                               ("a mailpanel with that name already exits"),
                               NULL);
        fprintf(stderr, "Existing entry\n");
        return;
      } else if (i->is_modified != DELETED) {
        pos++;
      }
    }
    new = create_configtab(ctabs->tabs, buf[0], NULL, pos, TRUE, 1);
    new->is_modified = NEW;
    for (i = ctabs; i->next != NULL; i = i->next);
    i->next = new;
  }
  gtk_clist_append(GTK_CLIST(data->list), buf);
  gtk_entry_set_text(GTK_ENTRY(data->entry), "");
}

static void
file_choosen(GtkWidget * w, GtkWidget * sel) {
  char *name;

  name = (char *) gtk_file_selection_get_filename(GTK_FILE_SELECTION(sel));
  gtk_entry_set_text(GTK_ENTRY(g_object_get_data(G_OBJECT(sel), "entry")),
                     name);
}

static void
browse_clicked(GtkWidget * button, GtkWidget * entry) {
  GtkWidget *sel;

  sel = gtk_file_selection_new(_(_("Please select a mailbox or maildir")));
  gtk_window_set_modal(GTK_WINDOW(sel), TRUE);

  gtk_file_selection_set_filename(GTK_FILE_SELECTION(sel), "~/");
  g_object_set_data(G_OBJECT(sel), "entry", entry);

  g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(sel)->ok_button),
                   "clicked", G_CALLBACK(file_choosen), sel);

  g_signal_connect_swapped(GTK_OBJECT(GTK_FILE_SELECTION(sel)->ok_button),
                           "clicked",
                           G_CALLBACK(gtk_widget_destroy), (gpointer) sel);

  g_signal_connect_swapped(GTK_OBJECT
                           (GTK_FILE_SELECTION(sel)->cancel_button),
                           "clicked", G_CALLBACK(gtk_widget_destroy),
                           (gpointer) sel);
  gtk_widget_show_all(sel);
}

static void
clist_delete(GtkWidget * button, Configtab * data) {
  gchar *buf;
  Configtab *i;
  GList *list;
  gint tabnum = 0;

  if (data->row < 0)
    return;

  gtk_clist_get_text(GTK_CLIST(data->list), data->row, 0, &buf);
  if (data->is_mailpanel) {
    for (list = data->mailbox_list; list; list = list->next)
      if (!strcmp(buf, (gchar *) list->data)) {
        free(list->data);
        data->mailbox_list = g_list_remove_link(data->mailbox_list, list);
        if (data->is_modified != NEW)
          data->is_modified = MODIFIED;
        break;
      }
  } else
    for (i = ctabs; i != NULL; i = i->next) {
      if (!strcmp(i->name, buf) && i->is_modified != DELETED) {
        i->is_modified = DELETED;
        gtk_notebook_remove_page(GTK_NOTEBOOK(i->tabs), tabnum);
        break;
      } else if (i->is_modified != DELETED)
        tabnum++;
    }

  gtk_clist_remove(GTK_CLIST(data->list), data->row);
  data->row = -1;
}

static void
button_toggle(GtkToggleButton * button, gint data) {
  if (button->active)
    ctoggles |= data;
  else
    ctoggles &= ~data;
}

static void
command_entry_changed(GtkEntry * entry, Configtab * data) {
  data->command = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
  if (data->is_modified != NEW) {
    data->is_modified = MODIFIED;
  }
}

static void
ticks_spin_changed(GtkSpinButton * spin, Configtab * data) {
  data->ticks = gtk_spin_button_get_value_as_int(spin);
  if (data->is_modified != NEW) {
    data->is_modified = MODIFIED;
  }
}

static void
canimations_changed(GtkAdjustment * adj, gpointer * data) {
  canimation_steps = (int) adj->value * 2;
}

static void
free_configtab(Configtab * config) {
  GList *list;

  free(config->name);
  free(config->command);

  for (list = config->mailbox_list; list; list = list->next)
    free(list->data);

  g_list_free(config->mailbox_list);
  free(config);
}


static void
mailwatch_config_destroyed(void) {
  Configtab *i;

  for (i = ctabs; i != NULL; i = ctabs) {
    ctabs = i->next;
    free_configtab(i);
  }
  /* i == NULL so ctabs is also 'reset to NULL */
}

static void
apply_plugin_config() {
  Configtab *i, *last, *next;
  GList *list;
  GList *mlist;
  Mailpanel *panel;

  toggles = ctoggles;
  animation_steps = canimation_steps;

  for (last = NULL, i = ctabs; i != NULL; i = next) {
    next = i->next;
    switch (i->is_modified) {
    case NMODIFIED:
      last = i;
      break;
    case MODIFIED:
      del_mailpathlist(i->name);
      for (list = i->mailbox_list; list; list = list->next) {
        add_mailpath(i->name, (gchar *) list->data);
      }
      change_command(i->name, i->command);
      change_ticks(i->name, i->ticks);
      i->is_modified = NMODIFIED;
      last = i;
      break;
    case NEW:
      panel = create_mailpanel(i->name);
      change_command(i->name, i->command);
      change_ticks(i->name, i->ticks);
      display_panel(panel, 1);
      for (list = i->mailbox_list; list; list = list->next) {
        add_mailpath(i->name, (gchar *) list->data);
      }
      i->is_modified = NMODIFIED;
      last = i;
      break;
    case DELETED:
      del_mailpanel(i->name);
      last->next = i->next;
      free_configtab(i);
      break;
    default:
      break;
    }
  }
  /* make sure all mailboxes are checked again */
  for (panel = mailpanels; panel; panel = panel->next)
    for (list = panel->mailpath_list; list; list = list->next) {
      mlist = ((Mailpath *) list->data)->mailbox_list = 0;
      for (; mlist; mlist = mlist->next)
        ((Mailbox *) mlist->data)->last_mtime = 0;
    }
}


static GtkWidget *
create_new_tab(GtkWidget * tabs, char *name, gint pos) {
  GtkWidget *frame;
  GtkWidget *label;
  GtkWidget *vbox;

  frame = gtk_frame_new(NULL);
  gtk_container_border_width(GTK_CONTAINER(frame), 3);
  gtk_widget_show(frame);

  label = gtk_label_new(name);
  if (pos)
    gtk_notebook_insert_page(GTK_NOTEBOOK(tabs), frame, label, pos);
  else
    gtk_notebook_append_page(GTK_NOTEBOOK(tabs), frame, label);

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_border_width(GTK_CONTAINER(vbox), 3);
  gtk_container_add(GTK_CONTAINER(frame), vbox);

  return vbox;
}


static Configtab *
create_configtab(GtkWidget * tabs, gchar * name, gchar * command,
                 gint pos, gint mailpanel, gint ticks) {

  GtkWidget *vbox;
  GtkWidget *vbox2;
  GtkWidget *seperator;
  GtkWidget *label;
  GtkWidget *button;
  GtkWidget *scrolled;
  GtkWidget *hbox;
  GtkWidget *entry;
  GtkWidget *spinbutton;
  Configtab *config;

  vbox = create_new_tab(tabs, name, pos);
  config = g_new0(Configtab, 1);
  config->name = strdup(name);
  config->command = (command == NULL ? NULL : strdup(command));
  config->ticks = ticks;
  config->is_mailpanel = mailpanel;
  config->row = -1;
  config->next = NULL;
  config->is_modified = NMODIFIED;
  config->mailbox_list = NULL;
  config->tabs = tabs;

  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show(hbox);

  config->entry = gtk_entry_new();
  gtk_box_pack_start(GTK_BOX(hbox), config->entry, TRUE, TRUE, 1);
  gtk_widget_show(config->entry);
  gtk_entry_set_text(GTK_ENTRY(config->entry), "");

  if (config->is_mailpanel) {
    button = gtk_button_new_with_label(_("Browse"));
    gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 5);
    gtk_widget_show(button);
    gtk_signal_connect(GTK_OBJECT(button), "clicked",
                       (GtkSignalFunc) browse_clicked, config->entry);
  }


  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show(hbox);

  seperator = gtk_hseparator_new();
  gtk_widget_show(seperator);
  gtk_box_pack_start(GTK_BOX(vbox), seperator, FALSE, FALSE, 3);

  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
  gtk_widget_show(hbox);

  scrolled = gtk_scrolled_window_new(NULL, NULL);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
                                 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  gtk_box_pack_start(GTK_BOX(hbox), scrolled, TRUE, TRUE, 0);
  gtk_widget_show(scrolled);

  config->list = gtk_clist_new_with_titles(1, &name);

  gtk_signal_connect(GTK_OBJECT(config->list), "select_row",
                     (GtkSignalFunc) clist_mailbox_selected, config);

  gtk_container_add(GTK_CONTAINER(scrolled), config->list);

  vbox2 = gtk_vbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(hbox), vbox2, FALSE, FALSE, 5);
  gtk_widget_show(vbox2);

  button = gtk_button_new_from_stock(GTK_STOCK_ADD);
  gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 5);
  gtk_widget_show(button);
  gtk_signal_connect(GTK_OBJECT(button), "clicked",
                     (GtkSignalFunc) clist_enter, config);

  button = gtk_button_new_from_stock(GTK_STOCK_DELETE);
  gtk_box_pack_start(GTK_BOX(vbox2), button, FALSE, FALSE, 5);
  gtk_signal_connect(GTK_OBJECT(button), "clicked",
                     (GtkSignalFunc) clist_delete, config);

  if (config->is_mailpanel) {
    seperator = gtk_hseparator_new();
    gtk_box_pack_start(GTK_BOX(vbox), seperator, FALSE, FALSE, 5);
    gtk_widget_show(seperator);

    hbox = gtk_hbox_new(FALSE, 0);

    label = gtk_label_new(_("Check every "));
    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);

    spinbutton = gtk_spin_button_new_with_range(1, 3600, 1);
    gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spinbutton), 0);
    gtk_signal_connect(GTK_OBJECT(spinbutton), "changed",
                       (GtkSignalFunc) ticks_spin_changed, config);
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinbutton), config->ticks);

    gtk_box_pack_start(GTK_BOX(hbox), spinbutton, FALSE, FALSE, 0);

    label = gtk_label_new(_("seconds"));
    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);

    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
    gtk_widget_show_all(hbox);

    hbox = gtk_hbox_new(FALSE, 0);
    label = gtk_label_new(_("run external command:"));
    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
    gtk_widget_show_all(hbox);
    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);

    entry = gtk_entry_new();
    if (config->command != NULL)
      gtk_entry_set_text(GTK_ENTRY(entry), config->command);
    gtk_entry_set_editable(GTK_ENTRY(entry), TRUE);
    gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 2);
    gtk_signal_connect(GTK_OBJECT(entry), "changed",
                       (GtkSignalFunc) command_entry_changed, config);
    gtk_widget_show(entry);

    gtk_widget_show(hbox);
  }
  gtk_widget_show(config->list);
  gtk_widget_show_all(vbox);
  return config;
}

static void
create_help_text(GtkWidget * text) {
  gkrellm_gtk_text_view_append(text,
                               _
                               ("this plugin lets you monitor multiple mailboxes..\n"
                                "it's supports mbox, maildir and MH style format\n\n"));
  gkrellm_gtk_text_view_append(text,
                               _("<b>How to configure:\n"
                                 "\tmailpanels tab:\n"));
  gkrellm_gtk_text_view_append(text,
                               _
                               ("\tin this tab you enter the names of the mailpanels you"
                                " want.\n"
                                "\tfor every mailpanel you add or delete a new tab will"
                                " appear/disappear\n\n"));
  gkrellm_gtk_text_view_append(text, _("<b>\ttabs for each mailpanel:\n"));
  gkrellm_gtk_text_view_append(text,
                               _
                               ("\tfor every mailpanel defined in the mailpanels tab, a tab"
                                " exists\n"
                                "\tin these tabs you must fill in every mailbox/maildir"
                                " that you want\n"
                                "\tto monitor in the tab's mailpanel.\n"
                                "\tIf you put a command in the run an external command"
                                " entry box, \n\tit will be run when you click the panel\n\n"));
  gkrellm_gtk_text_view_append(text, _("<b>\ttoggles tab:\n"));
  gkrellm_gtk_text_view_append(text,
                               _("\t-Show total mail count:\n"
                                 "\t\tToggles on and off the showing of the total number"
                                 "of mail.\n"
                                 "\t\tIf you use, maildir's and you have a lot of mail"
                                 " then this can put a little load on gkrellm\n"
                                 "\t-Count accessed, but unread mail as new:\n"
                                 "\t\ttoggles counting mails with O in the Status header"
                                 " as new.\n"
                                 "\t-Show tooltips:\n"
                                 "\t\ttoggles showing tooltips with mailcount per box.\n"
                                 "\t-Highlight new mail:\n"
                                 "\t\tShow mailpanels with new mail in alt_textcolor.\n"
                                 "\t\tYour theme must support this (see the readme for more info).\n"
                                 "\t-Number of times to blink when new mail arrives:\n"
                                 "\t\tWell, uhm what this does should be pretty obvious\n"));

}

static void
create_plugin_tab(GtkWidget * tab_vbox) {
  GtkWidget *tabs;
  GtkWidget *vbox;
  GtkWidget *text;
  GtkWidget *button;
  GtkWidget *hbox;
  Mailpanel *p;
  Mailpath *mpath;
  Configtab *i;
  GList *list;
  gchar *buf[2];
  gchar *plugin_about_text;
  GtkWidget *spin;
  GtkAdjustment *adj;

  /* set toggles variable ok */
  ctoggles = toggles;
  canimation_steps = animation_steps;
  /* Make a couple of tabs.  One for setup and one for info
   */

  tabs = gtk_notebook_new();
  gtk_notebook_set_tab_pos(GTK_NOTEBOOK(tabs), GTK_POS_TOP);
  gtk_box_pack_start(GTK_BOX(tab_vbox), tabs, TRUE, TRUE, 0);
  /* clean up after a cancel.. */
  gtk_signal_connect(GTK_OBJECT(tabs), "destroy",
                     GTK_SIGNAL_FUNC(mailwatch_config_destroyed), NULL);

  /* --Setup tabs */

  ctabs = create_configtab(tabs, _("mailpanels"), NULL, 0, FALSE, 0);

  /* load existing mailpanels */
  i = ctabs;
  for (p = mailpanels; p; p = p->next) {
    buf[0] = p->name;
    buf[1] = NULL;
    gtk_clist_append(GTK_CLIST(ctabs->list), buf);

    i->next = create_configtab(tabs, p->name, p->command, 0, TRUE, p->ticks);
    i = i->next;
    for (list = p->mailpath_list; list; list = list->next) {
      mpath = (Mailpath *) list->data;
      buf[0] = mpath->path;
      i->mailbox_list = g_list_append(i->mailbox_list, strdup(buf[0]));
      gtk_clist_append(GTK_CLIST(i->list), buf);
    }
  }

/*---------toggles tab----------*/

  vbox = create_new_tab(tabs, "toggles", 0);

  button = gtk_check_button_new_with_label(_("Show total mail count"));
  gtk_container_add(GTK_CONTAINER(vbox), button);
  gtk_toggle_button_set_state((GtkToggleButton *) button,
                              toggles & SHOW_TOTAL);
  gtk_signal_connect(GTK_OBJECT(button), "toggled",
                     (GtkSignalFunc) button_toggle,
                     GINT_TO_POINTER(SHOW_TOTAL));

  button =
    gtk_check_button_new_with_label(_
                                    ("Count accessed, but unread mail as new"));
  gtk_container_add(GTK_CONTAINER(vbox), button);
  gtk_toggle_button_set_state((GtkToggleButton *) button, toggles & O_IS_NEW);
  gtk_signal_connect(GTK_OBJECT(button), "toggled",
                     (GtkSignalFunc) button_toggle,
                     GINT_TO_POINTER(O_IS_NEW));

  button = gtk_check_button_new_with_label(_("Show tooltips"));
  gtk_container_add(GTK_CONTAINER(vbox), button);
  gtk_toggle_button_set_state((GtkToggleButton *) button,
                              toggles & SHOW_TIPS);
  gtk_signal_connect(GTK_OBJECT(button), "toggled",
                     (GtkSignalFunc) button_toggle,
                     GINT_TO_POINTER(SHOW_TIPS));

  button = gtk_check_button_new_with_label(_("Highlight new mail"));
  gtk_container_add(GTK_CONTAINER(vbox), button);
  gtk_toggle_button_set_state((GtkToggleButton *) button,
                              toggles & HLIGHT_NEW);
  gtk_signal_connect(GTK_OBJECT(button), "toggled",
                     (GtkSignalFunc) button_toggle,
                     GINT_TO_POINTER(HLIGHT_NEW));

  hbox = gtk_hbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(vbox), hbox);
  adj =
    (GtkAdjustment *) gtk_adjustment_new(canimation_steps / 2, 0, 999, 1, 1,
                                         0);
  spin = gtk_spin_button_new(adj, 1, 0);
  gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(spin), TRUE);
  gtk_widget_set_usize(spin, 60, 0);
  gtk_signal_connect(GTK_OBJECT(adj), "value_changed",
                     GTK_SIGNAL_FUNC(canimations_changed), NULL);
  gtk_box_pack_start(GTK_BOX(hbox), spin, FALSE, FALSE, 0);
  text = gtk_label_new(_("Number of times to blink when new mail arrives"));
  gtk_box_pack_start(GTK_BOX(hbox), text, FALSE, FALSE, 0);

/* --Info tab */
  vbox = create_new_tab(tabs, _("Info"), 0);
  text = gkrellm_gtk_scrolled_text_view(vbox, NULL,
                                        GTK_POLICY_AUTOMATIC,
                                        GTK_POLICY_AUTOMATIC);
  create_help_text(text);
/* --------- about text ---------*/
  plugin_about_text =
    g_strdup_printf(_("Mailwatchplugin %d.%d%s\n"
                      "GKrellM mailwatch Plugin\n\n"
                      "Copyright (C) 2000 Sjoerd Simons\n"
                      "sjoerd@luon.net\n"
                      "http://gkrellm.luon.net\n\n"
                      "Released under GNU Public Licence"),
                    MAILWATCH_MAJOR_VERSION,
                    MAILWATCH_MINOR_VERSION, MAILWATCH_EXTRA_VERSION);

  vbox = create_new_tab(tabs, _("About"), 0);
  text = gtk_label_new(plugin_about_text);
  gtk_container_add(GTK_CONTAINER(vbox), text);
  g_free(plugin_about_text);
}

/* load and save config */

static void
save_plugin_config(FILE * f) {
  Mailpanel *panel;
  GList *mpath;

  fprintf(f, "%s toggles %d\n", CONFIG_KEYWORD, toggles);
  fprintf(f, "%s animation_steps %d\n", CONFIG_KEYWORD, animation_steps);

  for (panel = mailpanels; panel; panel = panel->next) {
    fprintf(f, "%s mailpanel %s\n", CONFIG_KEYWORD, panel->name);
    if (panel->command != NULL)
      fprintf(f, "%s command %s\n", CONFIG_KEYWORD, panel->command);
    if (panel->ticks > 1)
      fprintf(f, "%s ticks %d\n", CONFIG_KEYWORD, panel->ticks);
    for (mpath = panel->mailpath_list; mpath; mpath = mpath->next)
      fprintf(f, "%s mailbox %s\n", CONFIG_KEYWORD,
              ((Mailbox *) mpath->data)->path);
  }
}

static void
load_plugin_config(gchar * arg) {
  gchar *command;
  gchar *p;
  Mailpanel *last_panel;

  for (p = arg; *p && isspace(*p); p++);  /* remove leading whitespace */
  for (; *p && !isspace(*p); p++);  /* find the end of the command */

  command = malloc((p - arg + 1) * sizeof(char));
  memset(command, '\0', p - arg + 1);
  memcpy(command, arg, p - arg);

  for (; *p && isspace(*p); p++); /* remove leading whitespace */
  /* p points to our argument string */

  if (!strcmp(command, "toggles"))
    toggles = atoi(p);
  else if (!strcmp(command, "mailpanel")) {
    create_mailpanel(p);
  } else if (!strcmp(command, "mailbox")) {
    for (last_panel = mailpanels; last_panel->next != NULL; last_panel = last_panel->next); /* find last panel */
    add_mailpath(last_panel->name, p);
  } else if (!strcmp(command, "command")) {
    for (last_panel = mailpanels; last_panel->next != NULL; last_panel = last_panel->next); /* find last panel */
    change_command(last_panel->name, p);

  } else if (!strcmp(command, "ticks")) {
    for (last_panel = mailpanels; last_panel->next != NULL; last_panel = last_panel->next); /* find last panel */
    change_ticks(last_panel->name, atoi(p));

  } else if (!strcmp(command, "animation_steps")) {
    animation_steps = atoi(p);
  }
  free(command);
}


/* The monitor structure tells GKrellM how to call the plugin routines.
*/
static GkrellmMonitor plugin_mon = {
  "Mailwatch",                  /* Name, for config tab.    */
  0,                            /* Id,  0 if a plugin       */
  create_plugin,                /* The create function      */
  update_plugin,                /* The update function      */
  create_plugin_tab,            /* The config tab create function   */
  apply_plugin_config,          /* Apply the config function        */

  save_plugin_config,           /* Save user config                     */
  load_plugin_config,           /* Load user config                     */
  CONFIG_KEYWORD,               /* config keyword                       */

  NULL,                         /* Undefined 2  */
  NULL,                         /* Undefined 1  */
  NULL,                         /* Undefined 0  */

  LOCATION,                     /* Insert plugin before this monitor                    */

  NULL,                         /* Handle if a plugin, filled in by GKrellM     */
  NULL                          /* path if a plugin, filled in by GKrellM       */
};


  /* All GKrellM plugins must have one global routine named gkrellm_init_plugin()
   * |  which returns a pointer to a filled in monitor structure.
   */
GkrellmMonitor *
gkrellm_init_plugin(void) {
  mailpanels = NULL;
  toggles = 0xFFFF;
#ifdef ENABLE_NLS
  bind_textdomain_codeset(PACKAGE, "UTF-8");
#endif /* ENABLE_NLS */
  style_id = gkrellm_add_meter_style(&plugin_mon, MW_STYLE_NAME);
  return &plugin_mon;
}
