/*
 * WMMail - Window Maker Mail
 *
 * Copyright (c) 1996, 1997, 1998  Per Liden
 * Copyright (c) 1997, 1998  Bryan Chan
 *
 * 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.
 *
 * properties.c: routines that read libPL-style properties (some stolen
 *               code courtesy of Peter Bentley's Malaprop
 *               (http://www.sorted.org/~pete/wmaker):
 * 
 * Copyright (c) 1998 Peter Bentley (pete@sorted.org)
 * 
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice and this permission notice
 * appear in all copies of the software and related documentation
 * 
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
 * 
 * IN NO EVENT SHALL PETER BENTLEY BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT
 * ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 *
 * $Id: properties.c,v 1.1 2000/07/02 20:38:01 bryan.chan Exp $
 *
 */

#include <stdlib.h>
#include <strings.h>
#include <pwd.h>
#include <proplist.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "wmmail.h"
#include "wmutil.h"
#include "list.h"


#define GET_ENTRY(x,y,z) { proplist_t sub_key = PLMakeString(z); \
                           y = PLGetDictionaryEntry(x, sub_key); \
                           PLRelease(sub_key); }

/* function prototypes */
static proplist_t domain_load(char *);

static int get_boolean(proplist_t, proplist_t, int *);
static int get_coord(proplist_t, proplist_t, int *, int *);
static int get_int(proplist_t, proplist_t, int *);

static int get_num_of_msg_mode(proplist_t, proplist_t, int *);

static int get_animations(proplist_t, proplist_t);
static int get_animation(proplist_t, proplist_t, int);
static void anim_add_frame(int, char *);
static void anim_load_frame(XpmIcon *, char *);

static int get_mailboxes(proplist_t, proplist_t);
static int init_mailbox(Mailbox *, proplist_t);


/* parse the WMMail domain; what's a cleaner, simpler way to do this? */
int parse_gnustep_domain(char *path)
{
  unsigned int  i, number_of_keys;
  proplist_t    keys, key, value;
  proplist_t    domain = domain_load(path);
  char          *str;

  if (domain != NULL && PLIsDictionary(domain))
  {
    if ( (keys = PLGetAllDictionaryKeys(domain)) != NULL )
    {
      number_of_keys = PLGetNumberOfElements(keys);
  
      for (i = 0; i < number_of_keys; i++)
      {
        key = PLGetArrayElement(keys, i);
        value = PLGetDictionaryEntry(domain, key);
        str = PLGetString(key);
  
        if (PLIsDictionary(value))
        {
          if (!strcasecmp("Animations", str))
          {
            get_animations(key, value);
          }
          else
            croak("unrecognized dictionary \"%s\" ignored", str);
        }
        else if (PLIsString(value))
        {
          if (!strcasecmp("DisableBeep", str))
          {
            get_boolean(key, value, &use_beep);
            use_beep = ! use_beep;  /* if yes, then don't use beep */
          }
          else if (!strcasecmp("DoubleClickTime", str))
          {
            get_int(key, value, &double_click_time);
          }
          else if (!strcasecmp("DisplayEachMailbox", str))
          {
            get_boolean(key, value, &display_names);
          }
          else if (!strcasecmp("DisplayMessageCount", str))
          {
            get_num_of_msg_mode(key, value, &num_of_msg_mode);
          }
          else if (!strcasecmp("DisplayColor", str))
          {
            wmmail_color = wstrdup(PLGetString(value));
          }
          else if (!strcasecmp("DisplayFont", str))
          {
            wmmail_font = XLoadFont(disp, PLGetString(value));
          }
          else if (!strcasecmp("ExecuteOnNew", str))
          {
            exec_on_new = wstrdup(PLGetString(value));
          }
          else if (!strcasecmp("ExecuteOnClick", str))
          {
            exec_on_click = wstrdup(PLGetString(value));
          }
          else
            croak("unrecognized key \"%s\" ignored", str);
        }
        else if (PLIsArray(value))
        {
          if (!strcasecmp("Mailboxes", str))
          {
            get_mailboxes(key, value);
          }
          else if (!strcasecmp("DisplayLocation", str))
          {
            get_coord(key, value, &num_of_msg_x, &num_of_msg_y);
          }
          else
            croak("unrecognized array \"%s\" ignored", str);
        }

        wfree(str);
      }
    }

    return True;
  }
  else
  {
    croak("defaults domain %s corrupted", path);
    return False;
  }
}


/* load the specified domain from disk */
static proplist_t domain_load(char *path)
{
  proplist_t  list = NULL;

  if (path)
  {
    list = PLGetProplistWithPath(path);
    wfree((void *) path);
  }

  return list;
}


/* get a boolean value from a property list entry */
static int get_boolean(proplist_t key, proplist_t value, int *addr)
{
  static int  data;
  char        *s;

  if (!PLIsString(value)) 
  {
    croak("wrong option format for key \"%s\"; boolean expected",
          PLGetString(key));
    return False;
  }

  s = PLGetString(value);

  if (!strcasecmp("Yes", s))
    data = True;
  else if (!strcasecmp("No", s))
    data = False;
  else
  {
    croak("wrong option format for key \"%s\"; boolean expected",
          PLGetString(key));
    return False;
  }

  wfree(s);

  if (addr)
    *addr = data;

  return True;
}


/* get a coordinate from a property list entry */
static int get_coord(proplist_t key, proplist_t value, int *addr_x, int *addr_y)
{
  int dimensions, data[2], i;

  if (!PLIsArray(value))
  {
    croak("wrong option format for key \"%s\"; coordinates expected",
          PLGetString(key));
    return False;
  }

  if (( dimensions = PLGetNumberOfElements(value) ) != 2)
  {
    croak("wrong option format for key \"%s\"; coordinates expected",
          PLGetString(key));
    return False;
  }

  for (i = 0; i < dimensions; i++)
  {
    proplist_t element = PLGetArrayElement(value, i);

    if (!PLIsString(element))
    {
      croak("wrong option format for key \"%s\"; coordinates expected",
            PLGetString(key));
      return False;
    }
    
    if (sscanf(PLGetString(element), "%i", &data[i]) != 1)
    {
      croak("integer pair expected for coordinates in key \"%s\"",
            PLGetString(key));
      return False;
    }
  }  

  *addr_x = data[0]; 
  *addr_y = data[1]; 

  return True;
}


/* get an integer from a property list entry */
static int get_int(proplist_t key, proplist_t value, int *addr)
{
  static int  data;
  char        *s;

  if (!PLIsString(value)) 
  {
    croak("wrong option format for key \"%s\"; integer expected",
          PLGetString(key));
    return False;
  }

  s = PLGetString(value);

  if (sscanf(s, "%i", &data) != 1)
  {
    croak("cannot convert \"%s\" to integer for key \"%s\"", 
          s, PLGetString(key));
    return False;
  }

  if (addr)
    *addr = data;

  return True;
}


static int get_num_of_msg_mode(proplist_t key, proplist_t value, int *addr)
{
  static int data;
  char       *s;

  if (!PLIsString(value)) 
  {
    croak("wrong option format for key \"%s\"; expected \"NewOnly\", "
          "\"TotalOnly\", \"NewOverTotal\", or \"None\"",
          PLGetString(key));
    return False;
  }

  s = PLGetString(value);

  if (!strcasecmp("None", s))
    data = SHOW_NONE;
  else if (!strcasecmp("NewOverTotal", s))
    data = SHOW_NEW_OVER_TOTAL;
  else if (!strcasecmp("NewOnly", s))
    data = SHOW_NEW_ONLY;
  else if (!strcasecmp("TotalOnly", s))
    data = SHOW_TOTAL_ONLY;
  else
  {
    croak("wrong option format for key \"%s\"; expected \"NewOnly\", "
          "\"TotalOnly\", \"NewOverTotal\", or \"None\"",
          PLGetString(key));
    return False;
  }

  if (addr)
    *addr = data;

  return True;
}


/* ANIMATIONS */

static int get_animations(proplist_t key, proplist_t value)
{
  proplist_t animation_properties[3];
  proplist_t delay_key, delay_value;
  proplist_t frame_key, frame_value;
  
  GET_ENTRY(value, animation_properties[NO_MAIL], "Empty");
  GET_ENTRY(value, animation_properties[OLD_MAIL], "Old");
  GET_ENTRY(value, animation_properties[NEW_MAIL], "New");

  delay_key = PLMakeString("Delay");
  frame_key = PLMakeString("Frames");

  if (animation_properties[NO_MAIL] != NULL)
  {
    delay_value = PLGetDictionaryEntry(animation_properties[NO_MAIL],
                                       delay_key);
    if (delay_value != NULL)
    {
      get_int(delay_key, delay_value, &anim_speed[NO_MAIL]);
      anim_speed[NO_MAIL] *= 100;
    }

    frame_value = PLGetDictionaryEntry(animation_properties[NO_MAIL],
                                       frame_key);
    if (frame_value != NULL)
      get_animation(frame_key, frame_value, NO_MAIL);
  }

  if (animation_properties[OLD_MAIL] != NULL)
  {
    delay_value = PLGetDictionaryEntry(animation_properties[OLD_MAIL],
                                       delay_key);
    if (delay_value != NULL)
    {
      get_int(delay_key, delay_value, &anim_speed[OLD_MAIL]);
      anim_speed[OLD_MAIL] *= 100;
    }

    frame_value = PLGetDictionaryEntry(animation_properties[OLD_MAIL],
                                       frame_key);
    if (frame_value != NULL)
      get_animation(frame_key, frame_value, OLD_MAIL);
  }

  if (animation_properties[NEW_MAIL] != NULL)
  {
    delay_value = PLGetDictionaryEntry(animation_properties[NEW_MAIL],
                                       delay_key);
    if (delay_value != NULL)
    {
      get_int(delay_key, delay_value, &anim_speed[NEW_MAIL]);
      anim_speed[NEW_MAIL] *= 100;
    }

    frame_value = PLGetDictionaryEntry(animation_properties[NEW_MAIL],
                                       frame_key);
    if (frame_value != NULL)
      get_animation(frame_key, frame_value, NEW_MAIL);
  }

  PLRelease(delay_key);
  PLRelease(frame_key);

  return True;
}


static int get_animation(proplist_t key, proplist_t frames, int type)
{
  int i, number_of_frames;

  if (!PLIsArray(frames))
  {
    croak("wrong option format for key \"%s\"; array expected",
          PLGetString(key));
    return False;
  }

  number_of_frames = PLGetNumberOfElements(frames);

  for (i = 0; i < number_of_frames; i++)
  {
    char *s = PLGetString(PLGetArrayElement(frames, i));
    anim_add_frame(type, s);
    wfree(s);
  }  

  return True;
}


static void anim_add_frame(int which, char *path)
{
  XpmIcon *current;

  if (animations[which] == NULL)
  {
    animations[which] = (XpmIcon *) wmalloc(sizeof(XpmIcon));
    current = animations[which];
  }
  else
  {
    for (current = animations[which]; 
         current->next != animations[which];	/* until wrapped around */
         current = current->next);

    current->next = (XpmIcon *) wmalloc(sizeof(XpmIcon));
    current = current->next;
  }

  anim_load_frame(current, path);
  current->next = animations[which];	/* make a looping animation */
  current->text_cycle = -1;  /* don't cycle text */
}


static void anim_load_frame(XpmIcon *anim, char *path)
{
  int ret;

  anim->attributes.valuemask   = (XpmExactColors | XpmCloseness);
  anim->attributes.exactColors = FALSE;
  anim->attributes.closeness   = 40000;

  ret = XpmReadFileToPixmap(disp, root, path, &anim->pixmap, &anim->mask,
                            &anim->attributes);

  switch(ret)
  {
    case XpmSuccess: 
      break;

    case XpmColorError: 
    case XpmColorFailed: 
      croak("not enough free colours loading '%s'", path);
      break;

    case XpmOpenFailed:
      croak("cannot open '%s'", path);
      break;

    case XpmFileInvalid:
      croak("invalid XPM file '%s'", path);
      break;

    case XpmNoMemory:
      croak("not enough free memory loading '%s'", path);
      break;

    default:
      croak("unknown error (%d) loading '%s'", ret, path);
      break;
  }

  if (ret < 0)
    exit(-1);

  if (anim->mask == 0)
  {
    croak("XPM file '%s' does not have a transparent colour");
    exit(-1);
  }
}


/* MAILBOXES */

static int get_mailboxes(proplist_t key, proplist_t value)
{
  int         i,
              number_of_mailboxes = PLGetNumberOfElements(value);
  Mailbox    *mailbox;
  proplist_t  mailbox_definition;

  if (number_of_mailboxes > 0)  
  {
    for (i = 0; i < number_of_mailboxes; i++)
    {
      mailbox_definition = PLGetArrayElement(value, i);

      if (PLIsDictionary(mailbox_definition))
      {
        mailbox = (Mailbox *) wmalloc(sizeof(Mailbox));

        /* parse mailbox definition; destroy the mailbox if it is bad */
        if (init_mailbox(mailbox, mailbox_definition))
        {
#ifdef DEBUG
          croak("mailbox \"%s\" added", mailbox->name);
#endif
          mailbox_list = list_cons(mailbox, mailbox_list);
          mailbox_count = list_length(mailbox_list);
        }
        else
        {
          croak("mailbox \"%s\" ignored", (mailbox->name ? mailbox->name : ""));
          wfree(mailbox->execute_on_update);
          wfree(mailbox->name);
          wfree(mailbox);
        }
      }
      else
      {
        /* skip this mailbox if its definition is corrupt */
        croak("wrong option format for key \"%s\"; mailbox definition "
              "expected", PLGetString(key));
      }
    }
  }

  return True;
}


static int init_mailbox(Mailbox *mailbox, proplist_t mailbox_definition)
{
  proplist_t  mailbox_name,
              mailbox_type,
              execute_on_update,
              update_interval;

  char       *mailbox_type_text;

  mailbox->status           = NO_MAIL;
  mailbox->total_mail_count = 0;
  mailbox->new_mail_count   = 0;
  mailbox->size             = -1;
  mailbox->last_update      = 0;

  GET_ENTRY(mailbox_definition, mailbox_name, "Name");
  if (mailbox_name != NULL)
  {
    if (!PLIsString(mailbox_name))
    {
      croak("wrong option format for key \"Name\"; string expected");
      return False;
    }
    else
      mailbox->name = PLGetString(mailbox_name);
  }
  else
  {
    croak("mailbox missing key \"Name\"; ignored");
    return False;
  }

  GET_ENTRY(mailbox_definition, mailbox_type, "Type");
  if (mailbox_type != NULL)
  {
    if (!PLIsString(mailbox_type))
    {
      croak("wrong option format for key \"Type\"; mailbox type expected");
      return False;
    }
    else
      mailbox_type_text = PLGetString(mailbox_type);
  
#ifdef MBOX_SUPPORT
    if (!strcasecmp(mailbox_type_text, "mbox"))
      mailbox->type = TYPE_MBOX;
    else
#endif
#ifdef MH_SUPPORT
    if (!strcasecmp(mailbox_type_text, "mh"))
      mailbox->type = TYPE_MH;
    else
#endif
#ifdef MAILDIR_SUPPORT
    if (!strcasecmp(mailbox_type_text, "maildir"))
      mailbox->type = TYPE_MAILDIR;
    else
#endif
#ifdef POP3_SUPPORT
    if (!strcasecmp(mailbox_type_text, "pop3"))
      mailbox->type = TYPE_POP3;
    else
#endif
#ifdef IMAP_SUPPORT
    if (!strcasecmp(mailbox_type_text, "imap"))
      mailbox->type = TYPE_IMAP;
    else
#endif
    {
      croak("unknown mailbox type \"%s\"; expected: "
#ifdef MBOX_SUPPORT
            "\"mbox\", "
#endif
#ifdef MH_SUPPORT
            "\"mh\", "
#endif
#ifdef MAILDIR_SUPPORT
            "\"MailDir\", "
#endif
#ifdef POP3_SUPPORT
            "\"POP3\", "
#endif
#ifdef IMAP_SUPPORT
            "\"IMAP\""
#endif
            , mailbox_type_text);

      return False;
    }
  }
  else
  {
    croak("mailbox \"%s\" missing key \"Type\"; ignored", mailbox->name);
    return False;
  }

  GET_ENTRY(mailbox_definition, execute_on_update, "ExecuteOnUpdate");
  if (execute_on_update != NULL)
  {
    if (!PLIsString(execute_on_update))
    {
      croak("wrong option format for key \"ExecuteOnUpdate\"; string expected");
      return False;
    }
    else
      mailbox->execute_on_update = PLGetString(execute_on_update);
  }
  else
    mailbox->execute_on_update = NULL;

  GET_ENTRY(mailbox_definition, update_interval, "UpdateInterval");
  {
    proplist_t label = PLMakeString("UpdateInterval");

    if (update_interval == NULL ||
        !get_int(label, update_interval, &mailbox->update_interval))
    {
      croak("mailbox \"%s\" missing key \"UpdateInterval\"; %d assumed",
            mailbox->name, DEFAULT_INTERVAL);
      mailbox->update_interval = DEFAULT_INTERVAL;
    }

    PLRelease(label);
  }

  GET_ENTRY(mailbox_definition, mailbox->options, "Options");
  if (mailbox->options == NULL)
  {
    croak("mailbox \"%s\" missing key \"Options\"; ignored", mailbox->name);
    return False;
  }
  else if (!PLIsDictionary(mailbox->options))
  {
    croak("wrong option format for key \"Options\"; dictionary expected");
    return False;
  }

  return True;
}




syntax highlighted by Code2HTML, v. 0.9.1