/* 
   MultiSync Opie Plugin - Synchronize Opie/Zaurus Devices
   Copyright (C) 2003 Tom Foottit <tom@foottit.com>

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License version 2 as
   published by the Free Software Foundation;

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
   SOFTWARE IS DISCLAIMED.
*/

/*
 *  $Id: opie_vtype.c,v 1.14 2004/03/30 04:56:26 irix Exp $
 */

#include "opie_vtype.h"
#include "opie_comms.h"
#include "opie_log.h"

#include <string.h>
#include <vobject.h>
#include <vcc.h>

#include <time.h>

/* bitmasks for the opie calendar recurring
 * weekday values 
 */
typedef enum {
  OPIE_MONDAY_BM=0xFE,
  OPIE_TUESDAY_BM=0xFD,
  OPIE_WEDNESDAY_BM=0xFB,
  OPIE_THURSDAY_BM=0xF7,
  OPIE_FRIDAY_BM=0xEF,
  OPIE_SATURDAY_BM=0xDF,
  OPIE_SUNDAY_BM=0xBF,
} opie_weekday_bitmask;
  
/* opie calendar recurring weekday values */  
typedef enum {      
  OPIE_MONDAY_VAL=0x1,
  OPIE_TUESDAY_VAL=0x2,
  OPIE_WEDNESDAY_VAL=0x4,
  OPIE_THURSDAY_VAL=0x8,
  OPIE_FRIDAY_VAL=0x10,
  OPIE_SATURDAY_VAL=0x20,
  OPIE_SUNDAY_VAL=0x40,
} opie_weekday_val;



/* convert a vcal format date to struct tm */
void vcal_date_to_tm(struct tm* date_tm, 
                     const char* vcal_date, 
                     gboolean* all_day);


/*
 * VObjectOErrorHander
 */
void VObjectOErrorHander(char *errstr)
{
  OPIE_DEBUG("VObjectO parse failed:");
  OPIE_DEBUG(errstr);  
}


/*
 * cal_data_to_vcal
 */
char* cal_data_to_vcal(cal_data* calendar, GList* categories)
{
  char* ret_vcal = NULL;
  char* versit_vcal = NULL;
  char* date_string = NULL;
  struct tm* date_tm; 
  
  VObjectO *prop;
  VObjectO *vcal;
  VObjectO *vevent;
  
  vcal = newVObjectO(VCCalPropO);
  vevent = addPropO(vcal, VCEventPropO);
  
  /* summary */
  if(calendar->summary)
    addPropValueO(vevent, VCSummaryPropO, calendar->summary);
  
  /* desc */
  if(calendar->desc)
  {
    addPropValueO(vevent, VCDescriptionPropO, calendar->desc);
    
    /* set the summary equal to the description if not present */
    if(!calendar->summary)
    {
      addPropValueO(vevent, VCSummaryPropO, calendar->desc);
    }
  }
  
  /* location */
  if(calendar->location)
    addPropValueO(vevent, VCLocationPropO, calendar->location);
   
  /* start date */
  if(calendar->start_date != 0)
  {
    date_tm = g_malloc0(sizeof(struct tm));
    date_tm = (struct tm*)localtime_r(&(calendar->start_date), date_tm);
    
    if(date_tm)
    {
      if(calendar->all_day)
      {
        date_string = g_strdup_printf("%04d%02d%02d",
                                      date_tm->tm_year + 1900,
                                      date_tm->tm_mon + 1,
                                      date_tm->tm_mday);
      }
      else
      {
        date_string = g_strdup_printf("%04d%02d%02dT%02d%02d%02d",
                                      date_tm->tm_year + 1900,
                                      date_tm->tm_mon + 1,
                                      date_tm->tm_mday,
                                      date_tm->tm_hour,
                                      date_tm->tm_min,
                                      date_tm->tm_sec);
      }
      
      prop = addPropValueO(vevent, VCDTstartPropO, date_string);
      
      /* set whether it is a date or a date and time */
      if(calendar->all_day)
      {
        addPropValueO(prop, VCValuePropO, "DATE");
      }
      else
      {
        addPropValueO(prop, VCValuePropO, "DATE-TIME");
      }
        

      g_free(date_tm);
    }
    
    g_free(date_string);
  }
    
  /* end date */
  if(calendar->end_date != 0)
  {
    date_tm = g_malloc0(sizeof(struct tm));
    date_tm = (struct tm*)localtime_r(&(calendar->end_date), date_tm);
    
    if(date_tm)
    {
      if(calendar->all_day)
      {
        date_string = g_strdup_printf("%04d%02d%02d",
                                      date_tm->tm_year + 1900,
                                      date_tm->tm_mon + 1,
                                      date_tm->tm_mday);
      }
      else
      {
        date_string = g_strdup_printf("%04d%02d%02dT%02d%02d%02d",
                                      date_tm->tm_year + 1900,
                                      date_tm->tm_mon + 1,
                                      date_tm->tm_mday,
                                      date_tm->tm_hour,
                                      date_tm->tm_min,
                                      date_tm->tm_sec);
      }

      prop = addPropValueO(vevent, VCDTendPropO, date_string);
      
      /* set whether it is a date or a date and time */
      if(calendar->all_day)
      {
        addPropValueO(prop, VCValuePropO, "DATE");
      }
      else
      {
        addPropValueO(prop, VCValuePropO, "DATE-TIME");
      }

      g_free(date_tm);
    }
  }  
    
  /* created date */
  if(calendar->created_date != 0)
  {
    date_tm = g_malloc0(sizeof(struct tm));
    date_tm = (struct tm*)localtime_r(&(calendar->created_date), date_tm);
    
    if(date_tm)
    {
      date_string = g_strdup_printf("%04d%02d%02dT%02d%02d%02d",
                                    date_tm->tm_year + 1900,
                                    date_tm->tm_mon + 1,
                                    date_tm->tm_mday,
                                    date_tm->tm_hour,
                                    date_tm->tm_min,
                                    date_tm->tm_sec);

      addPropValueO(vevent, VCDTStampPropO, date_string);

      g_free(date_tm);
      g_free(date_string);
    }
  }  
  
  /* alarm */
  if(calendar->alarm)
  {
    VObjectO *valarm, *trigger;
    char duration_unit;
    char* duration_str;
  
    valarm = addPropO(vevent, VCAlarmPropO);

    switch(calendar->alarm->time_type)
    {
      case ALARM_MIN:
        duration_unit = 'M';
        break;
      case ALARM_HOUR:
        duration_unit = 'H';
        break;
      case ALARM_DAY:
        duration_unit = 'D';
        break;
        
    }    
    
    /* trigger */
    duration_str = g_strdup_printf("-PT%u%c", 
                                   calendar->alarm->duration,
                                   duration_unit);
                                   
    trigger = addPropValueO(valarm, VCTriggerPropO, duration_str);
    g_free(duration_str);
    
    addPropValueO(trigger, VCValuePropO, "DURATION");
    
    if(calendar->alarm->related)
    {
      addPropValueO(trigger, VCRelatedPropO, calendar->alarm->related);
    }
    else
    {
      addPropValueO(trigger, VCRelatedPropO, "START");
    }
    
    /* action doesn't translate between opie and evo... */
    addPropValueO(valarm, VCActionPropO, "DISPLAY");
    
    /* desc */
    if(calendar->alarm->desc)
       addPropValueO(valarm, VCDescriptionPropO, calendar->alarm->desc);
    
  }
  
  /* categories */
  if(calendar->cids)
  {    
    /* deal with multiple categories */
    const char* cat_name = NULL;
    GString* cat_str = NULL;
    GList* current_cat = calendar->cids;
        
    while(current_cat != NULL) 
    { 
      if(current_cat == calendar->cids)
      {
        /* first time through */
        cat_str = g_string_new("");
        cat_name = opie_find_category(current_cat->data, categories);
        if(cat_name)
          g_string_sprintfa(cat_str, "%s", cat_name);
      }
      else
      { 
        cat_name = opie_find_category(current_cat->data, categories);
        if(cat_name)
          g_string_sprintfa(cat_str, ",%s", cat_name);
      }
      
      current_cat = current_cat->next;
    }
    
    prop = addPropValueO(vevent, VCCategoriesPropO, cat_str->str);
    g_string_free(cat_str, FALSE);
  }  
  
  /* recurrence */
  if(calendar->recurrence)
  {
    /* Evo does the RRULE such that the value of the RRULE is a bunch of semicolon
     * delimited attribute=value pairs, rather than having the attributes as properties
     * of the RRULE. Not sure if this is a bug or not, but it is pretty stupid.
     * Deal with it here.
     */
     
    VObjectO *vrrule;
    char* rrule;
    GString *rrulestr = g_string_new("");    
    
    /* type */
    switch(calendar->recurrence->type)
    {
      case RECURRENCE_DAILY:
        g_string_sprintfa(rrulestr, "%s=DAILY;", VCFreqPropO);
        break;
      case RECURRENCE_WEEKLY:
        g_string_sprintfa(rrulestr, "%s=WEEKLY;", VCFreqPropO);
        break;
      case RECURRENCE_MONTHLY:
        g_string_sprintfa(rrulestr, "%s=MONTHLY;", VCFreqPropO);
        break;
      case RECURRENCE_YEARLY:
        g_string_sprintfa(rrulestr, "%s=YEARLY;", VCFreqPropO);
        break;
    }
    
    /* until date */
    if(0 != calendar->recurrence->end_date)
    {
      date_tm = g_malloc0(sizeof(struct tm));
      date_tm = (struct tm*)localtime_r(&(calendar->recurrence->end_date), date_tm);
      
      if(date_tm)
      {
        date_string = g_strdup_printf("%04d%02d%02d",
                                      date_tm->tm_year + 1900,
                                      date_tm->tm_mon + 1,
                                      date_tm->tm_mday);
                                      
        g_string_sprintfa(rrulestr, "%s=%s;", VCUntilPropO, date_string);

        g_free(date_string);
        g_free(date_tm);
      }
    }
    
    /* interval */
    if(0 != calendar->recurrence->frequency)
    {
      g_string_sprintfa(rrulestr, "%s=%u;", VCIntervalPropO, calendar->recurrence->frequency);
    }
    
    /* position */
    if(0 != calendar->recurrence->position)
    {
      g_string_sprintfa(rrulestr, "%s=%u;", VCBySetPosPropO, calendar->recurrence->position);
    }
    
    /* days of the week */
    if(0 != calendar->recurrence->weekdays)
    {
      GString *weekdaystr = g_string_new("");
      char* weekdays;
      
      if((calendar->recurrence->weekdays | OPIE_SUNDAY_BM) == 0xFF)
      {
        g_string_append(weekdaystr, "SU,");
      }
      if((calendar->recurrence->weekdays | OPIE_MONDAY_BM) == 0xFF)
      {
        g_string_append(weekdaystr, "MO,");
      }
      if((calendar->recurrence->weekdays | OPIE_TUESDAY_BM) == 0xFF)
      {
        g_string_append(weekdaystr, "TU,");
      }
      if((calendar->recurrence->weekdays | OPIE_WEDNESDAY_BM) == 0xFF)
      {
        g_string_append(weekdaystr, "WE,");
      }
      if((calendar->recurrence->weekdays | OPIE_THURSDAY_BM) == 0xFF)
      {
        g_string_append(weekdaystr, "TH,");
      }
      if((calendar->recurrence->weekdays | OPIE_FRIDAY_BM) == 0xFF)
      {
        g_string_append(weekdaystr, "FR,");
      }
      if((calendar->recurrence->weekdays | OPIE_SATURDAY_BM) == 0xFF)
      {
        g_string_append(weekdaystr, "SA,");
      }
      
      weekdays = g_strdup(weekdaystr->str);
      if(strlen(weekdays) > 0)
      {
        /* chop off trailing comma */
        weekdays[(strlen(weekdays) - 1)] = '\0';
        
        /* set the value */
        g_string_sprintfa(rrulestr, "%s=%s;", VCByDayPropO, weekdays);
      }
      
      g_string_free(weekdaystr, FALSE);
      g_free(weekdays);
    }
    
    /* strip the trailing semicolon */
    rrule = g_strdup(rrulestr->str);
    rrule[(strlen(rrule) - 1)] = '\0';
    
    /* now that the string is ready, add the RRULE property */
    addPropValueO(vevent, VCRRulePropO, rrule);
    
    g_string_free(rrulestr, FALSE);

  } /* recurrence */
  
  /* convert VObjectO to string */
  versit_vcal = writeMemVObjectO(0,0,vcal);
  
  ret_vcal = g_strdup(versit_vcal);
  free(versit_vcal);
  deleteVObjectO(vcal);
  return ret_vcal;
}


/*
 * contact_data_to_vcard
 */
char* contact_data_to_vcard(contact_data* contact, GList* categories)
{
  char* ret_vcard = NULL;
  char* versit_vcard = NULL;
  GString* fullname;
  VObjectO *prop;
  VObjectO *vcard;
  GList* current_email = NULL;
  gchar** birthday_strings = NULL;
  gchar* vcard_bday_str = NULL;
  gchar** anniversary_strings = NULL;
  gchar* vcard_aday_str = NULL;
  
  vcard = newVObjectO(VCCardPropO);
  
  prop = addPropO(vcard,VCNamePropO);
  /* VCARD FN: only has one property, so we must add it all at
   * once, we'll be picking it up along the way */
  fullname = g_string_new("");
  /* first name */
  if(contact->first_name)
  {
    addPropValueO(prop, VCGivenNamePropO, contact->first_name);
    g_string_append(fullname,contact->first_name);
  }
  /* middle name */
  if(contact ->middle_name)
  {
    addPropValueO(prop, VCAdditionalNamesPropO, contact->middle_name);
    /* We're optimistic there won't be a middle without a first name, so we need the space */
    g_string_append(fullname," ");
  }
  /* last name */
  if(contact->last_name)
  {
    addPropValueO(prop, VCFamilyNamePropO, contact->last_name);
    if (fullname->len > 1)
    {
      /* We can't assume there's anything else before the last name */ 
      g_string_append(fullname," ");
    }
    g_string_append(fullname,contact->last_name);
  }
  /* suffix */
  if(contact->suffix)
  {
    addPropValueO(prop, VCNameSuffixesPropO, contact->suffix);
    /* Assume there's something else, otherwise it wouldn't be a suffix */
    g_string_append(fullname," ");
    g_string_append(fullname,contact->suffix);
  }
  /* full name */
  if(fullname->len > 1)
  {
    addPropValueO(vcard,VCFullNamePropO,fullname->str);
    g_string_free(fullname,FALSE);
  }

  /* file as */
  if(contact->file_as)
    addPropValueO(vcard, XEvoFileAsO, contact->file_as);
  else
  {
    /* file as not set - try something else */
    if(contact->first_name)
      addPropValueO(vcard, XEvoFileAsO, contact->first_name);
    else if(contact->last_name)
      addPropValueO(vcard, XEvoFileAsO, contact->last_name);
    else if(contact->company)
      addPropValueO(vcard, XEvoFileAsO, contact->company);
    else if(contact->department)
      addPropValueO(vcard, XEvoFileAsO, contact->department);
  }
  
  /* categories */
  if(contact->cids)
  {    
    /* deal with multiple categories */
    const char* cat_name = NULL;
    GString* cat_str = NULL;
    GList* current_cat = contact->cids;
        
    while(current_cat != NULL) 
    { 
      if(current_cat == contact->cids)
      {
        /* first time through */
        cat_str = g_string_new("");
        cat_name = opie_find_category(current_cat->data, categories);
        if(cat_name)
          g_string_sprintfa(cat_str, "%s", cat_name);
      }
      else
      { 
        cat_name = opie_find_category(current_cat->data, categories);
        if(cat_name)
          g_string_sprintfa(cat_str, ",%s", cat_name);
      }
      
      current_cat = current_cat->next;
    }
    
    prop = addPropValueO(vcard, VCCategoriesPropO, cat_str->str);
    g_string_free(cat_str, FALSE);
  }
  
  /* email */
  if(contact->emails)
  {
    if(contact->default_email) 
    {
      prop = addPropValueO(vcard, VCEmailAddressPropO, contact->default_email);
      (void) addPropO(prop, VCInternetPropO);
    }
    
    /* deal with multiple emails */
    current_email = contact->emails;
    while(current_email!=NULL) 
    {
      if(contact->default_email) 
      {
        if(!g_strcasecmp(contact->default_email, current_email->data))
        {
          current_email=current_email->next;
          continue;
	}
      }
      
      prop = addPropValueO(vcard, VCEmailAddressPropO, current_email->data);
      (void) addPropO(prop, VCInternetPropO);
      current_email=current_email->next;
    }
  }
  
  /* org */
  if(contact->company || contact->department)
    prop = addPropO(vcard,VCOrgPropO);
   
  /* company name */
  if(contact->company)
    addPropValueO(prop, VCOrgNamePropO, contact->company);  
    
  /* department name */
  if (contact->department)
     addPropValueO(prop, VCOrgUnitPropO, contact->department);
  
  /* home phone, fax, mobile */ 
  if(contact->home_phone)
  {
    prop = addPropValueO(vcard, VCTelephonePropO, contact->home_phone);
    (void) addPropO(prop, VCHomePropO);
  }
  if(contact->home_fax)
  {
    prop = addPropValueO(vcard, VCTelephonePropO, contact->home_fax);
    (void) addPropO(prop, VCFaxPropO);
    (void) addPropO(prop, VCHomePropO);
  }
  if(contact->home_mobile)
  {
    prop = addPropValueO(vcard, VCTelephonePropO, contact->home_mobile);
    (void) addPropO(prop, VCCellularPropO);
    /* (void) addPropO(prop, VCHomePropO);
     * above commented to handle evolution's cellular handling
     * it converts TEL;CELL;HOME to TEL;HOME which is stupid..
     */
  }
  
  /* web pages. vCard allows for several. Evolution only uses one, but we're not 
     concerned with Evolution alone. */
  if(contact->home_webpage)
  {
    prop = addPropValueO(vcard, VCURLPropO, contact->home_webpage);
    addPropO(prop, VCHomePropO);
  }
  if(contact->business_webpage)
  {
    prop = addPropValueO(vcard, VCURLPropO, contact->business_webpage);
    addPropO(prop, VCWorkPropO);
  }

  /* home address */
  if (contact->home_street || contact->home_city || contact->home_state || contact->home_country)
  {
    GString* label;
    prop = addPropO(vcard,VCAdrPropO);
    (void) addPropO(prop, VCHomePropO);
    /* Add mailing label so evolution has something to display.
     * This is currently hardwired to the US label layout used
     * by Evolution. We need to scoop up the parts and write out
     * the label as one later.*/
    label = g_string_new("");
    if(contact->home_street)
    {
      addPropValueO(prop, VCStreetAddressPropO, contact->home_street);
      g_string_append(label,contact->home_street);
      g_string_append(label,"\n");
    }
    if(contact->home_city)
    {
      addPropValueO(prop, VCCityPropO, contact->home_city);
      g_string_append(label,contact->home_city);
    }
    if(contact->home_state)
    {
      addPropValueO(prop, VCRegionPropO, contact->home_state);
      if (contact->home_city)
	g_string_append(label,", ");
      g_string_append(label, contact->home_state);
    }
    if(contact->home_zip)
    {
      addPropValueO(prop, VCPostalCodePropO, contact->home_zip);
      if (!contact->home_state && contact->home_city)
	g_string_append(label,", ");
      if (contact->home_state)
	g_string_append(label, " ");
      g_string_append(label, contact->home_zip);
    }
    if(contact->home_country)
    {
      addPropValueO(prop, VCCountryNamePropO, contact->home_country);
      g_string_append(label,"\n");
      g_string_append(label,contact->home_country);
    }
    /* Actually add the label */
    prop = addPropValueO(vcard,VCDeliveryLabelPropO,label->str);
    (void) addPropO(prop, VCHomePropO);
    (void) addPropO(prop, VCQuotedPrintablePropO);
    g_string_free(label,FALSE);
  }

  /* work phone, fax, mobile */ 
  if(contact->business_phone)
  {
    prop = addPropValueO(vcard, VCTelephonePropO, contact->business_phone);
    (void) addPropO(prop, VCWorkPropO);
  }
  if(contact->business_fax)
  {
    prop = addPropValueO(vcard, VCTelephonePropO, contact->business_fax);
    (void) addPropO(prop, VCFaxPropO);
    (void) addPropO(prop, VCWorkPropO);
  }
  if(contact->business_mobile)
  {
    prop = addPropValueO(vcard, VCTelephonePropO, contact->business_mobile);
    (void) addPropO(prop, VCCellularPropO);
    (void) addPropO(prop, VCWorkPropO);
  }
  if(contact->business_pager)
  {
    prop = addPropValueO(vcard, VCTelephonePropO, contact->business_pager);
    (void) addPropO(prop, VCPagerPropO);
    (void) addPropO(prop, VCWorkPropO);
  }
  
  /* misc. */
  if (contact->spouse)
     addPropValueO(vcard, XEvolutionSpouseO, contact->spouse);
  if (contact->assistant)
     addPropValueO(vcard, XEvolutionAssistantO, contact->assistant);
  if (contact->manager)
     addPropValueO(vcard, XEvolutionManagerO, contact->manager);
  if (contact->office)
     addPropValueO(vcard, XEvolutionOfficeO, contact->office);
  if (contact->nickname)
     addPropValueO(vcard, VCNicknameO, contact->nickname);
  if (contact->profession)
     addPropValueO(vcard, VCRolePropO, contact->profession);
  if (contact->jobtitle)
     addPropValueO(vcard, VCTitlePropO, contact->jobtitle);  
  
  /* work address */
  if (contact->business_street || contact->business_city || contact->business_state || contact->business_zip)
  {
    GString* label;
    prop = addPropO(vcard,VCAdrPropO);
    (void) addPropO(prop, VCWorkPropO);
    (void) addPropO(prop, VCHomePropO);
    /* Add mailing label so evolution has something to display.
     * This is currently hardwired to the US label layout used
     * by Evolution. We need to scoop up the parts and write out
     * the label as one later. This code is virtually identical to
     * that responsible for the home address, so it should be
     * refactored at some point in time */
    label = g_string_new("");
    if(contact->business_street)
    {
      addPropValueO(prop, VCStreetAddressPropO, contact->business_street);
      g_string_append(label,contact->business_street);
      g_string_append(label,"\n");
    }
    if(contact->business_city)
    {
      addPropValueO(prop, VCCityPropO, contact->business_city);
      g_string_append(label,contact->business_city);
    } 
    if(contact->business_state)
    {
      addPropValueO(prop, VCRegionPropO, contact->business_state);
      if (contact->business_city)
	g_string_append(label,", ");
      g_string_append(label,contact->business_state);
    }
    if(contact->business_zip)
    {
      addPropValueO(prop, VCPostalCodePropO, contact->business_zip);
      if (!contact->business_state && contact->business_city)
	g_string_append(label,", ");
      if (contact->business_state)
	g_string_append(label," ");
      g_string_append(label,contact->business_zip);
    }
    if(contact->business_country)
    {
      addPropValueO(prop, VCCountryNamePropO, contact->business_country);
      g_string_append(label,"\n");
      g_string_append(label,contact->business_country);
    }
    /* Actually add the label */
    prop = addPropValueO(vcard,VCDeliveryLabelPropO,label->str);
    (void) addPropO(prop, VCWorkPropO);
    (void) addPropO(prop, VCQuotedPrintablePropO);
    g_string_free(label,FALSE);
  }

  /* note */
  if(contact->notes)
  {
    prop = addPropValueO(vcard, VCCommentPropO, contact->notes);
    (void) addPropO(prop, VCQuotedPrintablePropO);
  }

  /* birthday */
  if (contact->birthday)
  {
    /* convert opie/zaurus's birthday string to vcard format */
    /* ATTENTION: it seems that at least the latest opie_addressbook allows both
       dd.mm.yyyy and yyyymmdd as date formats (and uses the latter when saving).
       This is braindead but we have to deal with it. */

    if (strchr(contact->birthday,'.')) /* Period formatted date */
    {
      birthday_strings = g_strsplit(contact->birthday,".",3);
      
      if (birthday_strings[0]!=NULL && 
	  birthday_strings[1]!=NULL &&
	  birthday_strings[2]!=NULL) 
      {
	vcard_bday_str = g_strdup_printf("%s-%02d-%02d",
					 birthday_strings[2],
					 atoi(birthday_strings[1]),
					 atoi(birthday_strings[0]));
	
	addPropValueO(vcard, VCBirthDatePropO, vcard_bday_str);
        
	g_free(vcard_bday_str);
      }
      g_strfreev(birthday_strings);
    }
    else /* Date is not split by periods, i.e. it should be in ISO 8601 format*/
    {
      vcard_bday_str = g_strdup(contact->birthday);
      /* We're hoping that a date of correct length is also otherwise correctly formatted */
      if (strlen(vcard_bday_str)== 8) 
	/* We take the lazy road here and just pass the date on in ISO 8601 instead of re-formatting it*/
	addPropValueO(vcard,VCBirthDatePropO, vcard_bday_str);
      g_free(vcard_bday_str);
    }
  }

  /* anniversary */
  if (contact->anniversary)
  {
    /* ATTENTION: Same warning as for birthday applies regarding date format! */
    /* convert opie/zaurus's anniversary string to vcard format */
    if (strchr(contact->anniversary,'.')) /* Period formatted date */
    {
      anniversary_strings = g_strsplit(contact->anniversary,".",3);
      
      if (anniversary_strings[0]!=NULL && 
	  anniversary_strings[1]!=NULL &&
	  anniversary_strings[2]!=NULL) 
	{
	  vcard_aday_str = g_strdup_printf("%s-%02d-%02d",
					   anniversary_strings[2],
					   atoi(anniversary_strings[1]),
					   atoi(anniversary_strings[0]));
      
	  addPropValueO(vcard, XEvolutionAnniversaryO, vcard_aday_str);
      
	  g_free(vcard_aday_str);
	}
      g_strfreev(anniversary_strings);
    }
    else /* Date is not split by periods, i.e. it should be in ISO 8601 format*/
    {
      vcard_aday_str = g_strdup(contact->anniversary);
      /* We're hoping that a date of correct length is also otherwise correctly formatted */
      if (strlen(vcard_aday_str)== 8)
      { 
	/* We cannot take the lazy road here as Evolution does not accept ISO 8601 dates for this field */
	char* mystring;
	mystring = g_strdup_printf("%s-%s-%s",
				   g_strndup(vcard_aday_str,4),
				   g_strndup(vcard_aday_str+4,2),
				   g_strndup(vcard_aday_str+6,2));
	addPropValueO(vcard,XEvolutionAnniversaryO, mystring);
	g_free(mystring);
      }
      g_free(vcard_aday_str);
    }
  }  
      
  /* convert VObjectO to string */
  versit_vcard = writeMemVObjectO(0,0,vcard);
  
  ret_vcard = g_strdup(versit_vcard);
  free(versit_vcard);
  deleteVObjectO(vcard);
  return ret_vcard;
}


/*
 * todo_data_to_vtodo
 */
char* todo_data_to_vtodo(todo_data* todo, GList* categories)
{
  char* ret_vtodo = NULL;
  char* versit_vtodo = NULL;
  char* date_string = NULL;
  VObjectO *prop;
  VObjectO *vtodo;
  VObjectO *vcal;
  
  vcal = newVObjectO(VCCalPropO);
  vtodo = addPropO(vcal, VCTodoPropO);
  
  /* completed */
  if((todo->completed) && (!strcmp(todo->completed, "1")))
  {
    /* evo expects completed as a date */
    struct tm* date_tm;
    time_t now = time(0);
    date_tm = g_malloc0(sizeof(struct tm));    
    date_tm = (struct tm*)localtime_r(&now, date_tm);
    date_string = g_strdup_printf("%04d%02d%02dT%02d%02d%02dZ",
                                  date_tm->tm_year + 1900,
                                  date_tm->tm_mon + 1,
                                  date_tm->tm_mday,
                                  date_tm->tm_hour,
                                  date_tm->tm_min,
                                  date_tm->tm_sec);
    
    addPropValueO(vtodo, VCCompletedPropO, date_string);
    addPropValueO(vtodo, VCStatusPropO, "COMPLETED");
    
    /* set progress to 100 too */
    if(todo->progress)
      g_free(todo->progress);
    
    todo->progress = g_strdup("100");
    
    g_free(date_string);
    g_free(date_tm);
  }
  
  /* priority */
  if(todo->priority)
  {
    switch (atoi(todo->priority))
    {
      case 1:
      addPropValueO(vtodo, VCPriorityPropO, "1");
      break;
      case 2:
      addPropValueO(vtodo, VCPriorityPropO, "3");
      break;
      case 3:
      addPropValueO(vtodo, VCPriorityPropO, "5");
      break;
      case 4:
      addPropValueO(vtodo, VCPriorityPropO, "7");
      break;
      case 5:
      addPropValueO(vtodo, VCPriorityPropO, "9");
      break;
    }
  }
  
  /* progress */
  if(todo->progress)
    addPropValueO(vtodo, VCPctCompletePropO, todo->progress);
    
  /* desc */
  if(todo->desc)
  {
    addPropValueO(vtodo, VCDescriptionPropO, todo->desc);
    
    /* make sure summary is set */
    if(!todo->summary)
      addPropValueO(vtodo, VCSummaryPropO, todo->desc);
  }
  
  /* summary */
  if(todo->summary)
  {
    addPropValueO(vtodo, VCSummaryPropO, todo->summary);
    
    if(!todo->desc)
      addPropValueO(vtodo, VCDescriptionPropO, todo->summary);
  }
  
  /* due date */ 
  if(todo->dateyear && todo->datemonth && todo->dateday)
  {
    date_string = g_strdup_printf("%s%02d%02d",
                                  todo->dateyear,
                                  atoi(todo->datemonth),
                                  atoi(todo->dateday));
    prop = addPropValueO(vtodo, VCDuePropO, date_string);
    addPropValueO(prop, VCValuePropO, "DATE");
    
    g_free(date_string);                               
  }
      
  /* categories */
  if(todo->cids)
  {    
    /* deal with multiple categories */
    const char* cat_name = NULL;
    GString* cat_str = NULL;
    GList* current_cat = todo->cids;
        
    while(current_cat != NULL) 
    { 
      if(current_cat == todo->cids)
      {
        /* first time through */
        cat_str = g_string_new("");
        cat_name = opie_find_category(current_cat->data, categories);
        if(cat_name)
          g_string_sprintfa(cat_str, "%s", cat_name);
      }
      else
      { 
        cat_name = opie_find_category(current_cat->data, categories);
        if(cat_name)
          g_string_sprintfa(cat_str, ",%s", cat_name);
      }
      
      current_cat = current_cat->next;
    }
    
    prop = addPropValueO(vtodo, VCCategoriesPropO, cat_str->str);
    g_string_free(cat_str, FALSE);
  }  
  
  /* TODO - start date? */
  
  /* convert VObjectO to string */
  versit_vtodo = writeMemVObjectO(0,0,vcal);
  
  ret_vtodo = g_strdup(versit_vtodo);
  free(versit_vtodo);
  deleteVObjectO(vcal);
  return ret_vtodo;
}


/*
 * vcal_to_cal_data
 */
cal_data* vcal_to_cal_data(char* vcal, GList** categories)
{
  VObjectO *v, *t, *a, *rrule, *vcalobj;
  VObjectIteratorO i, j, k, l;
  cal_data* cal;
  const char* n;
  char* date_str;
  struct tm* date_tm;

  /* sanity check */
  if(!vcal || (strlen(vcal) == 0))
  {
    return NULL; 
  }
  
  cal = g_malloc0(sizeof(cal_data));
  
  registerMimeErrorHandlerO(VObjectOErrorHander);
  vcalobj = Parse_MIMEO(vcal, strlen(vcal));
  
  if(NULL == vcalobj)
  {
    /* parse failed */
    return NULL;
  }
  
  initPropIteratorO(&i,vcalobj);
  
  while(moreIterationO(&i))
  {
    v = nextVObjectO(&i);
    n = vObjectNameO(v);
    
    if(n)
    {
      /* event is a subobject of vcal */
      if(strcmp(n,VCEventPropO) == 0)
      {
        /* get the properties of the event */
        initPropIteratorO(&j,v);
        while(moreIterationO(&j))
        {      
          t = nextVObjectO(&j);
          n = vObjectNameO(t);

          if(strcmp(n,VCSummaryPropO) == 0)
          {
            cal->summary = g_strdup(fakeCStringO(vObjectUStringZValueO(t)));
          }    
          else if(strcmp(n,VCDescriptionPropO) == 0)
          {
            cal->desc = g_strdup(fakeCStringO(vObjectUStringZValueO(t)));
          }    
          else if(strcmp(n,VCLocationPropO) == 0)
          {
            cal->location = g_strdup(fakeCStringO(vObjectUStringZValueO(t)));
          }    
          else if(strcmp(n,VCDTstartPropO) == 0)
          {
            date_str = g_strdup(fakeCStringO(vObjectUStringZValueO(t)));

            if(date_str)
            {
              gboolean all_day = FALSE;

              date_tm = g_malloc0(sizeof(struct tm));
              vcal_date_to_tm(date_tm, date_str, &all_day);

              cal->start_date = mktime(date_tm);
              cal->all_day = all_day;

              g_free(date_str);
              g_free(date_tm);
            }
          }        
          else if(strcmp(n,VCDTendPropO) == 0)
          {
            date_str = g_strdup(fakeCStringO(vObjectUStringZValueO(t)));

            if(date_str)
            {
              gboolean all_day = FALSE;

              date_tm = g_malloc0(sizeof(struct tm));
              vcal_date_to_tm(date_tm, date_str, &all_day);

              cal->end_date = mktime(date_tm);
              cal->all_day = all_day;

              g_free(date_str);
              g_free(date_tm);
            }
          }        
          else if(strcmp(n,VCDTStampPropO) == 0)
          {
            date_str = g_strdup(fakeCStringO(vObjectUStringZValueO(t)));

            if(date_str)
            {
              date_tm = g_malloc0(sizeof(struct tm));
              vcal_date_to_tm(date_tm, date_str, NULL);

              cal->created_date = mktime(date_tm);

              g_free(date_str);
              g_free(date_tm);
            }
          }    
              
          /* categories */
          else if(strcmp(n,VCCategoriesPropO) == 0)
          {
            int j = 0;
            const char* cid;
            gchar** cat_strings = g_strsplit(fakeCStringO(vObjectUStringZValueO(t)),",",20);

            for(j=0; cat_strings[j] != NULL; ++j)
            {
              cid = opie_add_category(cat_strings[j], categories);
              if(cid)
                cal->cids = g_list_append(cal->cids, g_strdup(cid));
            }
          }

          /* alarm */
          else if(strcmp(n,VCAlarmPropO) == 0)
          {
            cal->alarm = g_malloc0(sizeof(alarm_data));

            /* get the properties of the alarm */
            initPropIteratorO(&k,t);
            while(moreIterationO(&k))
            {      
              a = nextVObjectO(&k);
              n = vObjectNameO(a);

              if(strcmp(n,VCTriggerPropO) == 0)
              {
                /* trigger has related, time_type and duration */
                char time_type;
                char* dur_str = NULL;
                int dur_str_len = 0;
                char* trigger_str = g_strdup(fakeCStringO(vObjectUStringZValueO(a)));

                if(trigger_str)
                {
                  /* eg trigger_str for alarm 15 min beforehand:
                   * -PT15M
                   * Need to extract the 'M' for units and the
                   * value 15 for duration.
                   */

                  switch(trigger_str[strlen(trigger_str)-1])
                  {
                    case 'M':
                      cal->alarm->time_type = ALARM_MIN;
                      break;
                    case 'H':
                      cal->alarm->time_type = ALARM_HOUR;
                      break;
                    case 'D':
                      cal->alarm->time_type = ALARM_DAY;
                      break;
                    default:
                      cal->alarm->time_type = ALARM_MIN;
                      break;  
                  }

                  dur_str_len = strlen(trigger_str) - 4;
                  if(dur_str_len > 0)
                  {
                    dur_str = g_malloc0(dur_str_len + 1);
                    memcpy(dur_str, trigger_str+4, dur_str_len);
                    dur_str[dur_str_len] = 0;

                    cal->alarm->duration = atoi(dur_str);
                    g_free(dur_str);
                  }
                }

                /* TODO - extract related ... */

              }
              else if(strcmp(n,VCActionPropO) == 0)
              {
                /* action doesn't translate opie -> evo - hardcode */
                cal->alarm->action_type = ALARM_ACTION_LOUD;
              }
              else if(strcmp(n,VCDescriptionPropO) == 0)
              {
                cal->alarm->desc = g_strdup(fakeCStringO(vObjectUStringZValueO(a)));
              }


            } /* while */

          } /* alarm */
          
          /* recurrence */
          else if(strcmp(n,VCRRulePropO) == 0)
          {
            int i;
            gchar** rruletokens;
            
            cal->recurrence = g_malloc0(sizeof(recurrence_data));
            
            /* Evo does the RRULE such that the value of the RRULE is a bunch of semicolon
             * delimited attribute=value pairs, rather than having the attributes as properties
             * of the RRULE. Not sure if this is a bug or not, but it is pretty stupid.
             * Deal with it here.
             */
            
            rruletokens = g_strsplit(fakeCStringO(vObjectUStringZValueO(t)), ";", 5);
                  
	    for(i=0; rruletokens[i]!=NULL; i++) 
            { 
              char* attr = NULL;
              char* val = NULL;     
              gchar** attrval = g_strsplit(rruletokens[i], "=", 2);
              attr = attrval[0];
              val = attrval[1];
              
              if(strcmp(attr,VCFreqPropO) == 0)
              {
                /* frequency */
                char* freq_str = g_strdup(val);
                
                if(!strcasecmp(freq_str, "DAILY"))
                {
                  cal->recurrence->type = RECURRENCE_DAILY;
                }
                else if(!strcasecmp(freq_str, "WEEKLY"))
                {
                  cal->recurrence->type = RECURRENCE_WEEKLY;
                }
                else if(!strcasecmp(freq_str, "MONTHLY"))
                {
                  cal->recurrence->type = RECURRENCE_MONTHLY;
                }
                else if(!strcasecmp(freq_str, "YEARLY"))
                {
                  cal->recurrence->type = RECURRENCE_YEARLY;
                }
                
                g_free(freq_str);
              }
              else if(strcmp(attr,VCUntilPropO) == 0)
              {
                /* end date */
                date_str = g_strdup(val);
                
                if(date_str)
                {
                  gboolean all_day = TRUE;
                  date_tm = g_malloc0(sizeof(struct tm));
                  vcal_date_to_tm(date_tm, date_str, &all_day);

                  cal->recurrence->end_date = mktime(date_tm);

                  g_free(date_str);
                  g_free(date_tm);
                }
              }
              else if(strcmp(attr,VCIntervalPropO) == 0)
              {
                /* frequency */
                char* freq_str = g_strdup(val);
                
                if(freq_str)
                {
                  cal->recurrence->frequency = strtoul(freq_str, NULL, 10);
                  g_free(freq_str);
                }
              }
              else if(strcmp(attr,VCBySetPosPropO) == 0)
              {
                /* position */
                char* pos_str = g_strdup(val);
                
                if(pos_str)
                {
                  cal->recurrence->position = strtoul(pos_str, NULL, 10);
                  g_free(pos_str);
                }
              }
              else if(strcmp(attr,VCByDayPropO) == 0)
              {
                /* weekdays */
                char* weekdays_str = g_strdup(val);
                
                if(weekdays_str)
                {
                  int j, weekdayval;
                  gchar** weekdaystokens = g_strsplit(weekdays_str, ",", 7);
                  
                  weekdayval = 0;
                  
	          for(j=0; weekdaystokens[j]!=NULL; j++) 
                  {
                    if(!strcasecmp(weekdaystokens[j], "SU"))
                    {
                      weekdayval += OPIE_SUNDAY_VAL;
                    }
                    else if(!strcasecmp(weekdaystokens[j], "MO"))
                    {
                      weekdayval += OPIE_MONDAY_VAL;
                    }
                    else if(!strcasecmp(weekdaystokens[j], "TU"))
                    {
                      weekdayval += OPIE_TUESDAY_VAL;
                    }
                    else if(!strcasecmp(weekdaystokens[j], "WE"))
                    {
                      weekdayval += OPIE_WEDNESDAY_VAL;
                    }
                    else if(!strcasecmp(weekdaystokens[j], "TH"))
                    {
                      weekdayval += OPIE_THURSDAY_VAL;
                    }
                    else if(!strcasecmp(weekdaystokens[j], "FR"))
                    {
                      weekdayval += OPIE_FRIDAY_VAL;
                    }
                    else if(!strcasecmp(weekdaystokens[j], "SA"))
                    {
                      weekdayval += OPIE_SATURDAY_VAL;
                    }
	          } /* for */
                  
	          g_strfreev(weekdaystokens);
                  g_free(weekdays_str);
                  
                  if(weekdayval > 0)
                  {
                    cal->recurrence->weekdays = weekdayval;
                  }
                }
              } /* weekdays */
              
              g_strfreev(attrval);
              
            } /* for */
            
            g_strfreev(rruletokens);

          } /* recurrence */          

        } /* while */
      }
    } /* if n */    
  } /* while */
      
  cleanVObjectO(vcalobj);

  return cal;
}


/*
 * vcard_to_contact_data
 */
contact_data* vcard_to_contact_data(char* vcard, GList** categories)
{
  VObjectO *v, *prop, *vcontact;
  VObjectIteratorO i;
  contact_data* contact;
  const char* n;
  gchar** birthday_strings;
  gboolean using_evo_fileas = FALSE;

  /* sanity check */
  if(!vcard || (strlen(vcard) == 0))
  {
    return NULL; 
  }
  
  contact = g_malloc0(sizeof(contact_data));
  
  registerMimeErrorHandlerO(VObjectOErrorHander);
  vcontact = Parse_MIMEO(vcard, strlen(vcard));
  
  initPropIteratorO(&i,vcontact);
  
  while(moreIterationO(&i))
  {
    v = nextVObjectO(&i);
    n = vObjectNameO(v);
  
    /* name */
    if(strcmp(n,VCNamePropO) == 0)
    {
      /* last name */
      prop = isAPropertyOfO(v, VCFamilyNamePropO);
      if(prop)
      {
        contact->last_name = g_strdup(fakeCStringO(vObjectUStringZValueO(prop)));
      }  
        
      /* middle name */
      prop = isAPropertyOfO(v, VCAdditionalNamesPropO);
      if(prop)
      {
	contact->middle_name = g_strdup(fakeCStringO(vObjectUStringZValueO(prop)));
      }

      /* first name */
      prop = isAPropertyOfO(v, VCGivenNamePropO);
      if(prop)
      {
        contact->first_name = g_strdup(fakeCStringO(vObjectUStringZValueO(prop)));
      }
      /* suffix */
      prop = isAPropertyOfO(v, VCNameSuffixesPropO);
      if(prop)
      {
	contact->suffix = g_strdup(fakeCStringO(vObjectUStringZValueO(prop)));
      }
    }
  
    /* file as */
    else if(strcmp(n,XEvoFileAsO) == 0)
    {
      if(contact->file_as)
        g_free(contact->file_as);

      contact->file_as = g_strdup(fakeCStringO(vObjectUStringZValueO(v)));
      using_evo_fileas = TRUE;      
    }
    else if(strcmp(n,VCFullNamePropO) == 0)
    {
      /* let the Evolution File-As extension take precedence */
      if(!using_evo_fileas)
      {
        contact->file_as = g_strdup(fakeCStringO(vObjectUStringZValueO(v)));
      }  
    }
      
    /* email */
    else if(strcmp(n,VCEmailAddressPropO) == 0)
    {
      /* the first email specified will be the default */
      if (contact->emails == NULL) 
      {
      	contact->default_email = g_strdup(fakeCStringO(vObjectUStringZValueO(v)));      
      }
      contact->emails = g_list_append(contact->emails,
                                      g_strdup(fakeCStringO(vObjectUStringZValueO(v)))); 
    }  
   
    /* org */
    else if(strcmp(n,VCOrgPropO) == 0)
    {
      prop = isAPropertyOfO(v, VCOrgNamePropO);
      if(prop)
      {
        contact->company = g_strdup(fakeCStringO(vObjectUStringZValueO(prop)));
      }
       
      prop = isAPropertyOfO(v, VCOrgUnitPropO);
      if (prop) 
      {
	 contact->department = g_strdup(fakeCStringO(vObjectUStringZValueO(prop)));
      }
       
    }    
    
    /* phone, fax, mobile */ 
    else if(strcmp(n,VCTelephonePropO) == 0)
    {
      prop = isAPropertyOfO(v, VCHomePropO);
      if(prop)
      {
        /* home */
        prop = isAPropertyOfO(v, VCFaxPropO);
        if(prop)
        {
          contact->home_fax = g_strdup(fakeCStringO(vObjectUStringZValueO(v)));          
        }
        else
        {
          prop = isAPropertyOfO(v, VCCellularPropO);
          if(prop)
          {
            contact->home_mobile = g_strdup(fakeCStringO(vObjectUStringZValueO(v)));
          }
          else
          {
            contact->home_phone = g_strdup(fakeCStringO(vObjectUStringZValueO(v)));
          }       
        }
      }
      
      prop = isAPropertyOfO(v, VCWorkPropO);
      if(prop)
      {
        /* work */
        prop = isAPropertyOfO(v, VCFaxPropO);
        if(prop)
        {
          contact->business_fax = g_strdup(fakeCStringO(vObjectUStringZValueO(v)));          
        }
        else
        {
          prop = isAPropertyOfO(v, VCCellularPropO);
          if(prop)
          {
            contact->business_mobile = g_strdup(fakeCStringO(vObjectUStringZValueO(v)));
          }
          else
          {
            prop = isAPropertyOfO(v, VCPagerPropO);
            if(prop)
            {
              contact->business_pager = g_strdup(fakeCStringO(vObjectUStringZValueO(v)));
            }
            else
            {
              contact->business_phone = g_strdup(fakeCStringO(vObjectUStringZValueO(v)));
            }       
          }       
        }
      }
      
      /* a hack to sort out evolution's handling of mobiles */
      if (isAPropertyOfO(v,VCCellularPropO) &&
          !isAPropertyOfO(v,VCHomePropO) &&
          !isAPropertyOfO(v,VCWorkPropO)) 
      {
        contact->home_mobile=g_strdup(fakeCStringO(vObjectUStringZValueO(v)));
      }      
      
    }

    /* address */    
    else if(strcmp(n,VCAdrPropO) == 0)
    {
      prop = isAPropertyOfO(v, VCHomePropO);
      if(prop)
      {
        /* home */
        prop = isAPropertyOfO(v, VCStreetAddressPropO);
        if(prop)
        {
          contact->home_street = g_strdup(fakeCStringO(vObjectUStringZValueO(prop)));          
        }
        prop = isAPropertyOfO(v, VCExtAddressPropO);
        if(prop)
        {
          if (contact->home_street) 
          {
            gchar *old = contact->home_street;
            contact->home_street = g_strconcat(old, " - ", fakeCStringO(vObjectUStringZValueO(prop)), NULL);
            g_free(old);         
          }
          else
          {
            contact->home_street = g_strdup(fakeCStringO(vObjectUStringZValueO(prop)));
          }
        }        
        prop = isAPropertyOfO(v, VCCityPropO);
        if(prop)
        {
          contact->home_city = g_strdup(fakeCStringO(vObjectUStringZValueO(prop)));          
        }
        prop = isAPropertyOfO(v, VCRegionPropO);
        if(prop)
        {
          contact->home_state = g_strdup(fakeCStringO(vObjectUStringZValueO(prop)));          
        }
        prop = isAPropertyOfO(v, VCPostalCodePropO);
        if(prop)
        {
          contact->home_zip = g_strdup(fakeCStringO(vObjectUStringZValueO(prop)));          
        }
        prop = isAPropertyOfO(v, VCCountryNamePropO);
        if(prop)
        {
          contact->home_country = g_strdup(fakeCStringO(vObjectUStringZValueO(prop)));          
        }
      }
      
      prop = isAPropertyOfO(v, VCWorkPropO);
      if(prop)
      {
        /* work */
        prop = isAPropertyOfO(v, VCStreetAddressPropO);
        if(prop)
        {
          contact->business_street = g_strdup(fakeCStringO(vObjectUStringZValueO(prop)));          
        }
        prop = isAPropertyOfO(v, VCExtAddressPropO);
        if(prop)
        {
          if (contact->business_street)
          {
            gchar *old = contact->business_street;
            contact->business_street = g_strconcat(old, " - ", fakeCStringO(vObjectUStringZValueO(prop)), NULL);          
            g_free(old);
          }
          else
          {
            contact->business_street = g_strdup(fakeCStringO(vObjectUStringZValueO(prop)));
          }
        }
        prop = isAPropertyOfO(v, VCCityPropO);
        if(prop)
        {
          contact->business_city = g_strdup(fakeCStringO(vObjectUStringZValueO(prop)));          
        }
        prop = isAPropertyOfO(v, VCRegionPropO);
        if(prop)
        {
          contact->business_state = g_strdup(fakeCStringO(vObjectUStringZValueO(prop)));          
        }
        prop = isAPropertyOfO(v, VCPostalCodePropO);
        if(prop)
        {
          contact->business_zip = g_strdup(fakeCStringO(vObjectUStringZValueO(prop)));          
        }
        prop = isAPropertyOfO(v, VCCountryNamePropO);
        if(prop)
        {
          contact->business_country = g_strdup(fakeCStringO(vObjectUStringZValueO(prop)));          
        }
      }
            
    }

    /* URLs */
    else if(strcmp(n, VCURLPropO) == 0)
    {
      prop = isAPropertyOfO(v, VCWorkPropO);
      if (prop)
      {
	contact->business_webpage = g_strdup(fakeCStringO(vObjectUStringZValueO(prop)));
      }      
      else /* assume that any non-work URL is a home/private URL */
	contact->home_webpage = g_strdup(fakeCStringO(vObjectUStringZValueO(v)));
    }
    
    /* note */
    else if(strcmp(n,VCCommentPropO) == 0)
    {
      contact->notes = g_strdup(fakeCStringO(vObjectUStringZValueO(v)));
    }    
    
    /* spouse */
    else if(strcmp(n,XEvolutionSpouseO) == 0)
    {
      contact->spouse = g_strdup(fakeCStringO(vObjectUStringZValueO(v)));
    }
     
    /* assistant */
    else if(strcmp(n,XEvolutionAssistantO) == 0)
    {
      contact->assistant = g_strdup(fakeCStringO(vObjectUStringZValueO(v)));
    }    
    
    /* manager */
    else if(strcmp(n,XEvolutionManagerO) == 0)
    {
      contact->manager = g_strdup(fakeCStringO(vObjectUStringZValueO(v)));
    }
    
    /* office */
    else if(strcmp(n,XEvolutionOfficeO) == 0)
    {
      contact->office = g_strdup(fakeCStringO(vObjectUStringZValueO(v)));
    }
     
    /* profession */
    else if(strcmp(n,VCRolePropO) == 0)
    {
      contact->profession = g_strdup(fakeCStringO(vObjectUStringZValueO(v)));
    }
     
    /* job title */
    else if(strcmp(n,VCTitlePropO) == 0)
    {
      contact->jobtitle = g_strdup(fakeCStringO(vObjectUStringZValueO(v)));
    }
     
    /* Nickname */
    else if(strcmp(n,VCNicknameO) == 0)
    {
      contact->nickname = g_strdup(fakeCStringO(vObjectUStringZValueO(v)));
    }
    
    /* birthday */
    else if(strcmp(n,VCBirthDatePropO) == 0)
    {
      birthday_strings = g_strsplit(fakeCStringO(vObjectUStringZValueO(v)),"-",3);
      
      if (birthday_strings[0]!=NULL && 
          birthday_strings[1]!=NULL && 
          birthday_strings[2]!=NULL) 
      {
        contact->birthday = g_strdup_printf("%d.%d.%s",
                                            atoi(birthday_strings[2]),
			                    atoi(birthday_strings[1]),
                                            birthday_strings[0]);
      }
      
      g_strfreev(birthday_strings);
    }

    /* anniversary. This will only work with Evolution and other Applications supporting X-EVOLUTION-ANNIVERSARY */
    else if(strcmp(n,XEvolutionAnniversaryO) == 0)
    {
      birthday_strings = g_strsplit(fakeCStringO(vObjectUStringZValueO(v)),"-",3);
      
      if (birthday_strings[0]!=NULL && 
          birthday_strings[1]!=NULL && 
          birthday_strings[2]!=NULL) 
      {
        contact->anniversary = g_strdup_printf("%d.%d.%s",
                                            atoi(birthday_strings[2]),
			                    atoi(birthday_strings[1]),
                                            birthday_strings[0]);
      }
      
      g_strfreev(birthday_strings);
    }
    
    /* categories */
    else if(strcmp(n,VCCategoriesPropO) == 0)
    {
      int j = 0;
      char* cid = NULL;
      gchar** cat_strings = g_strsplit(fakeCStringO(vObjectUStringZValueO(v)),",",20);
      
      for(j=0; cat_strings[j] != NULL; ++j)
      {
        cid = opie_add_category(cat_strings[j], categories);
        
        if(cid)
          contact->cids = g_list_append(contact->cids, g_strdup(cid));
      }
      
      g_strfreev(cat_strings);
    }
    
  } /* while */
      
  cleanVObjectO(vcontact);

  return contact;
}


/*
 * vtodo_to_todo_data
 */
todo_data* vtodo_to_todo_data(char* vtodo, GList** categories)
{
  VObjectO *v, *t, *vtodoobj;
  VObjectIteratorO i, j;
  todo_data* todo;
  const char* n;
  char* date_str;
  
  /* sanity check */
  if(!vtodo || (strlen(vtodo) == 0))
  {
    return NULL; 
  }
  
  todo = g_malloc0(sizeof(todo_data));
  registerMimeErrorHandlerO(VObjectOErrorHander);
  vtodoobj = Parse_MIMEO(vtodo, strlen(vtodo));
  
  if(NULL == vtodoobj)
  {
    return NULL;
  }
  
  initPropIteratorO(&i,vtodoobj);
  
  while(moreIterationO(&i))
  {
    v = nextVObjectO(&i);
    n = vObjectNameO(v);
    
    /* todo is a subobject of vcal */
    if(strcmp(n,VCTodoPropO) == 0)
    {
      /* get the properties of the todo */
      initPropIteratorO(&j,v);
      while(moreIterationO(&j))
      {      
        t = nextVObjectO(&j);
        n = vObjectNameO(t);
        
        if(strcmp(n,VCPriorityPropO) == 0)
        {
          switch(atoi(fakeCStringO(vObjectUStringZValueO(t))))
          {
            case 1:
              todo->priority = g_strdup("1");
              break;
            case 3:
              todo->priority = g_strdup("2");
              break;
            case 5:
              todo->priority = g_strdup("3");
              break;
            case 7:
              todo->priority = g_strdup("4");
              break;
            case 9:
              todo->priority = g_strdup("5");
              break;
          }
        }    
        else if(strcmp(n,VCStatusPropO) == 0)
        {
          if(!strcmp(fakeCStringO(vObjectUStringZValueO(t)), "COMPLETED"))
          {
            todo->completed = g_strdup("1");
          }  
        }
        else if(strcmp(n,VCPctCompletePropO) == 0)
        {
          todo->progress = g_strdup(fakeCStringO(vObjectUStringZValueO(t)));
        }    
        else if(strcmp(n,VCSummaryPropO) == 0)
        {
          todo->summary = g_strdup(fakeCStringO(vObjectUStringZValueO(t)));
        }
        else if(strcmp(n,VCDescriptionPropO) == 0)
        {
          todo->desc = g_strdup(fakeCStringO(vObjectUStringZValueO(t)));
        }
        else if(strcmp(n,VCDuePropO) == 0)
        {
          date_str = g_strdup(fakeCStringO(vObjectUStringZValueO(t)));
          
          if(date_str && (strlen(date_str) == 8))
          {
            todo->hasdate= g_strdup("1");

            /* date is in format "YYYYMMDD" */
            todo->dateyear = g_malloc0(5);
            todo->datemonth = g_malloc0(3);
            todo->dateday = g_malloc0(3);
            memcpy(todo->dateyear, date_str, 4);
            todo->dateyear[4] = 0;
            memcpy(todo->datemonth, date_str+4, 2);
            todo->datemonth[2] = 0;
            memcpy(todo->dateday, date_str+6, 2);
            todo->dateday[2] = 0;          

            g_free(date_str);
          }
        }
        
        /* categories */
        else if(strcmp(n,VCCategoriesPropO) == 0)
        {
          int j = 0;
          const char* cid = NULL;
          gchar** cat_strings = g_strsplit(fakeCStringO(vObjectUStringZValueO(t)),",",20);

          for(j=0; cat_strings[j] != NULL; ++j)
          {
            cid = opie_add_category(cat_strings[j], categories);
            if(cid)
              todo->cids = g_list_append(todo->cids, g_strdup(cid));
          }
        }
        
      } /* while */
    }
    
        
  
  } /* while */
      
  cleanVObjectO(vtodoobj);

  return todo;
}


/*
 * vcal_date_to_tm
 */
void vcal_date_to_tm(struct tm* date_tm, const char* vcal_date, gboolean* all_day)
{
  char tmp[5];
  
  if((NULL == date_tm) || (NULL == vcal_date))
    return;
  
  /* date is in format "YYYYMMDD<T>HHMMSS" 
   * or just YYYYMDD if an all-day appointment
   */
  if(strlen(vcal_date) >= 8)
  {
    /* year */
    memcpy(tmp, vcal_date, 4);
    tmp[4] = 0;
    date_tm->tm_year = (atoi(tmp) - 1900);

    /* month */
    memcpy(tmp, vcal_date+4, 2);
    tmp[2] = 0;
    date_tm->tm_mon = (atoi(tmp) - 1);

    /* day */
    memcpy(tmp, vcal_date+6, 2);
    tmp[2] = 0;
    date_tm->tm_mday = atoi(tmp);

    if(strlen(vcal_date) >= 15)
    {
      /* hour */
      memcpy(tmp, vcal_date+9, 2);
      tmp[2] = 0;
      date_tm->tm_hour = atoi(tmp);

      /* min */
      memcpy(tmp, vcal_date+11, 2);
      tmp[2] = 0;
      date_tm->tm_min = atoi(tmp);

      /* sec */
      memcpy(tmp, vcal_date+13, 2);
      tmp[2] = 0;
      date_tm->tm_sec = atoi(tmp);
    }
    else
    {
      /* no time specified, so must be all day */
      if(NULL != all_day)
      {
        *all_day = TRUE;
      }
    }
    
    /* let mktime determine if it is DST or not */
    date_tm->tm_isdst = -1;
  }

}
