1356 lines
43 KiB
C
1356 lines
43 KiB
C
/************************************************************************
|
|
* Copyright (C) 2000 Thomas Schulz *
|
|
* */
|
|
static char rcsid[] =
|
|
"$Id: datavca.c,v 1.46 2001/03/02 02:09:59 tsch Rel $"; /*
|
|
* *
|
|
* IC35 synchronize data import/export: vCard,vCalendar format *
|
|
* *
|
|
*************************************************************************
|
|
* *
|
|
* ??? is "fixme" mark: sections of code needing fixes *
|
|
* *
|
|
* rev_dtime yyyy-mm-ddThh:mm:ss for VCARRD *
|
|
* mod_dtime yyyymmddThhmmss for VEVENT,VTODO,memo *
|
|
* IC35 records to/from vCard,vCalendar *
|
|
* ic35addr_to_vcard
|
|
* vcard_to_ic35addr
|
|
* ic35sched_to_vevent
|
|
* vevent_to_ic35sched
|
|
* ic35todo_to_vtodo
|
|
* vtodo_to_ic35todo
|
|
* ic35memo_to_vmemo
|
|
* vmemo_to_ic35memo
|
|
* vCard,vCal access functions *
|
|
* _vca_prodid local: vCal program korganizer,gnomecal
|
|
* _vca_rewind local: rewind record list using iterator
|
|
* _vca_getnext local: get next record using iterator
|
|
* vca_open
|
|
* vca_close
|
|
* vca_rewind
|
|
* vca_getrec
|
|
* vca_getrec_byID
|
|
* vca_updic35rec
|
|
* vca_cmpic35rec
|
|
* vca_putic35rec
|
|
* vca_delrec
|
|
* vca_recid
|
|
* vca_set_recid
|
|
* vca_recstat
|
|
* vca_set_recstat
|
|
* *
|
|
************************************************************************/
|
|
|
|
#include <stdio.h> /* sprintf(), .. */
|
|
#include <string.h> /* strcpy(), .. */
|
|
#include <ctype.h> /* isdigit(), .. */
|
|
#include <unistd.h> /* access() */
|
|
#include <sys/types.h> /* size_t, .. */
|
|
#include <time.h> /* struct tm, time() .. */
|
|
|
|
#include "util.h" /* ERR, uchar, .. */
|
|
#include "vcc.h" /* VObject, .. */
|
|
#include "vcutil.h" /* vCard,vCal utilities */
|
|
#include "ic35frec.h" /* IC35 record fields.. */
|
|
#include "dataio.h"
|
|
NOTUSED(rcsid);
|
|
|
|
|
|
/* record status values for X-PILOTSTAT */
|
|
#define VCA_CLEAN PIM_CLEAN
|
|
#define VCA_DIRTY PIM_DIRTY
|
|
/* vCalendar program ids */
|
|
#define VCAL_GNOME 1
|
|
#define VCAL_KDE 2
|
|
|
|
|
|
static int _vca_prodid( void );
|
|
static void vca_set_recid( VObject * vobj, ulong recid );
|
|
static void vca_set_recstat( VObject * vobj, int stat );
|
|
|
|
|
|
/* ============================================ */
|
|
/* IC35 Address record to/from vCard */
|
|
/* ============================================ */
|
|
/*
|
|
* field mapping:
|
|
* VCNameProp preferred
|
|
* VCGivenNameProp A_FirstName
|
|
* VCFamilyNameProp A_LastName
|
|
* VCFullNameProp only if VCNameProp absent
|
|
* first field A_FirstName
|
|
* last field A_LastName
|
|
* VCBirthDateProp A_BirthDate
|
|
* VCAdrProp
|
|
* VCStreetAddressProp A_Street
|
|
* VCCityProp A_City
|
|
* VCRegionProp A_Region
|
|
* VCPostalCodeProp A_ZIP
|
|
* VCCountryNameProp A_Country
|
|
* VCTelephoneProp
|
|
* VCHomeProp A_TelHome
|
|
* VCTelephoneProp
|
|
* VCWorkProp A_TelWork
|
|
* VCTelephoneProp
|
|
* VCCellularProp A_TelMobile
|
|
* VCTelephoneProp
|
|
* VCFaxProp A_TelFax
|
|
* VCEmailAddressProp
|
|
* VCInternetProp A_Email1
|
|
* VCEmailAddressProp
|
|
* VCInternetProp A_Email2
|
|
* VCOrgProp
|
|
* VCOrgNameProp A_Company
|
|
* VCURLProp A_URL
|
|
* VCNoteProp A_Notes
|
|
* "(def1):<cont>\n" A_Def1
|
|
* "(def2):<cont>\n" A_Def2
|
|
* -/- A_CategoryID default: 0x00
|
|
* VCCategoriesProp A_Category default: "Unfiled"
|
|
* use first known standard from multiple (Business,Personal,Unfiled)
|
|
* if single use it, even if non-standard
|
|
*/
|
|
|
|
/* convert IC35 Address record to vCard
|
|
* ------------------------------------
|
|
*/
|
|
static void
|
|
ic35addr_to_vcard( IC35REC * rec, VObject * vcard )
|
|
{
|
|
VObject * vprop;
|
|
char * fullname, *oldstr, *ic35first, *ic35last;
|
|
|
|
ic35first = ic35recfld( rec, A_FirstName );
|
|
ic35last = ic35recfld( rec, A_LastName );
|
|
vprop = SetProp( vcard, VCNameProp);
|
|
if ( (fullname = dupStringValue( vcard, VCFullNameProp )) && *fullname ) {
|
|
/*
|
|
* if old vCard (is present and) has fullname VCFullNameProp "FN:"
|
|
* substitute old VCFamilyNameProp in its value with A_FirstName
|
|
* and old VCGivenNameProp in it with A_LastName. no changes to
|
|
* VCFullNameProp will be made if VCGivenNameProp,VCFamilyNameProp
|
|
* are not substrings of it.
|
|
*/
|
|
fullname = dupSubst( oldstr = fullname,
|
|
StringValue( vprop, VCFamilyNameProp ), ic35last );
|
|
deleteStr( oldstr );
|
|
fullname = dupSubst( oldstr = fullname,
|
|
StringValue( vprop, VCGivenNameProp ), ic35first );
|
|
deleteStr( oldstr );
|
|
} else {
|
|
/*
|
|
* otherwise (no old vCard or no VCFullNameProp in it) construct
|
|
* VCFullNameProp from A_FirstName,A_LastName separated by space.
|
|
* if either A_FirstName or A_LastName is missing construct from
|
|
* the other one if present.
|
|
*/
|
|
if ( ic35first && *ic35first && ic35last && *ic35last ) {
|
|
fullname = dupStr( ic35first, strlen(ic35first)+1+strlen(ic35last) );
|
|
strcat( strcat( fullname, " " ), ic35last );
|
|
} else
|
|
fullname = dupStr( ic35first && *ic35first ? ic35first :
|
|
ic35last && *ic35last ? ic35last : "", 0 );
|
|
}
|
|
SetString( vcard, VCFullNameProp, fullname );
|
|
deleteStr( fullname );
|
|
SetString( vprop, VCGivenNameProp, ic35first );
|
|
SetString( vprop, VCFamilyNameProp, ic35last );
|
|
|
|
SetString( vcard, VCBirthDateProp, ic35recfld( rec, A_BirthDate ) );
|
|
|
|
if ( strlen( ic35recfld( rec, A_Street ) ) != 0
|
|
|| strlen( ic35recfld( rec, A_City ) ) != 0
|
|
|| strlen( ic35recfld( rec, A_Region ) ) != 0
|
|
|| strlen( ic35recfld( rec, A_ZIP ) ) != 0
|
|
|| strlen( ic35recfld( rec, A_Country ) ) != 0 ) {
|
|
vprop = SetProp( vcard, VCAdrProp );
|
|
SetString( vprop, VCPostalBoxProp, "" );
|
|
SetString( vprop, VCExtAddressProp, "" );
|
|
SetString( vprop, VCStreetAddressProp,ic35recfld( rec, A_Street ) );
|
|
SetString( vprop, VCCityProp, ic35recfld( rec, A_City ) );
|
|
SetString( vprop, VCRegionProp, ic35recfld( rec, A_Region ) );
|
|
SetString( vprop, VCPostalCodeProp, ic35recfld( rec, A_ZIP ) );
|
|
SetString( vprop, VCCountryNameProp, ic35recfld( rec, A_Country ) );
|
|
} else
|
|
DelProp( vcard, VCAdrProp );
|
|
|
|
SetTel( vcard, VCHomeProp, ic35recfld( rec, A_TelHome ) );
|
|
SetTel( vcard, VCWorkProp, ic35recfld( rec, A_TelWork ) );
|
|
SetTel( vcard, VCCellularProp, ic35recfld( rec, A_TelMobile ) );
|
|
SetTel( vcard, VCFaxProp, ic35recfld( rec, A_TelFax ) );
|
|
|
|
SetEmail( vcard, 1, ic35recfld( rec, A_Email1 ) );
|
|
SetEmail( vcard, 2, ic35recfld( rec, A_Email2 ) );
|
|
|
|
if ( strlen( ic35recfld( rec, A_Company ) ) != 0 ) {
|
|
vprop = SetProp( vcard, VCOrgProp );
|
|
SetString( vprop, VCOrgNameProp, ic35recfld( rec, A_Company ) );
|
|
} else
|
|
DelProp( vcard, VCOrgProp );
|
|
|
|
SetString( vcard, VCURLProp, ic35recfld( rec, A_URL ) );
|
|
|
|
SetNoteProp( vcard, DEF1PROP, ic35recfld( rec, A_Def1 ) );
|
|
SetNoteProp( vcard, DEF2PROP, ic35recfld( rec, A_Def2 ) );
|
|
SetNotes( vcard, ic35recfld( rec, A_Notes ) );
|
|
|
|
SetCategory( vcard, ic35recfld( rec, A_Category ) );
|
|
}
|
|
|
|
/* convert vCard to IC35 address record
|
|
* ------------------------------------
|
|
*/
|
|
static void
|
|
vcard_to_ic35addr( VObject * vcard, IC35REC * rec )
|
|
{
|
|
VObject * vprop;
|
|
VObjectIterator iter;
|
|
char * strval;
|
|
char * pstr;
|
|
const char * name;
|
|
int emailnum;
|
|
|
|
if ( (vprop = isAPropertyOf( vcard, VCNameProp )) != NULL ) {
|
|
set_ic35recfld( rec, A_LastName,
|
|
StringValue( vprop, VCFamilyNameProp ) );
|
|
set_ic35recfld( rec, A_FirstName,
|
|
StringValue( vprop, VCGivenNameProp ) );
|
|
} else if ( (strval = StringValue( vcard, VCNameProp )) && *strval ) {
|
|
/* no Name property, parse first,last field from FormattedName */
|
|
if ( (pstr = strrchr( strval, ' ' )) != NULL ) {
|
|
set_ic35recfld( rec, A_LastName, pstr+1 );
|
|
pstr = strchr( strval, ' ' );
|
|
*pstr = '\0';
|
|
set_ic35recfld( rec, A_FirstName, strval );
|
|
} else
|
|
set_ic35recfld( rec, A_LastName, strval );
|
|
} else {
|
|
set_ic35recfld( rec, A_LastName, "NoName" ); /* IC35 needs name */
|
|
}
|
|
vprop = isAPropertyOf( vcard, VCAdrProp );
|
|
set_ic35recfld( rec, A_City,
|
|
StringValue( vprop, VCCityProp ) );
|
|
set_ic35recfld( rec, A_Street,
|
|
StringValue( vprop, VCStreetAddressProp ) );
|
|
set_ic35recfld( rec, A_Region,
|
|
StringValue( vprop, VCRegionProp ) );
|
|
set_ic35recfld( rec, A_ZIP,
|
|
StringValue( vprop, VCPostalCodeProp ) );
|
|
set_ic35recfld( rec, A_Country,
|
|
StringValue( vprop, VCCountryNameProp ) );
|
|
vprop = isAPropertyOf( vcard, VCOrgProp );
|
|
set_ic35recfld( rec, A_Company,
|
|
StringValue( vprop, VCOrgNameProp ) );
|
|
|
|
emailnum = 0;
|
|
set_ic35recfld( rec, A_TelHome, "" );
|
|
set_ic35recfld( rec, A_TelWork, "" );
|
|
set_ic35recfld( rec, A_TelMobile, "" );
|
|
set_ic35recfld( rec, A_TelFax, "" );
|
|
set_ic35recfld( rec, A_Email1, "" );
|
|
set_ic35recfld( rec, A_Email2, "" );
|
|
initPropIterator( &iter, vcard );
|
|
while ( moreIteration( &iter ) ) {
|
|
vprop = nextVObject( &iter );
|
|
name = vObjectName( vprop );
|
|
if ( strcasecmp( name, VCTelephoneProp ) == 0 ) {
|
|
if ( isAPropertyOf( vprop, VCHomeProp ) )
|
|
set_ic35recfld( rec, A_TelHome, StringValue( vprop, NULL ) );
|
|
else if ( isAPropertyOf( vprop, VCWorkProp ) )
|
|
set_ic35recfld( rec, A_TelWork, StringValue( vprop, NULL ) );
|
|
else if ( isAPropertyOf( vprop, VCCellularProp ) )
|
|
set_ic35recfld( rec, A_TelMobile, StringValue( vprop, NULL ) );
|
|
else if ( isAPropertyOf( vprop, VCFaxProp ) )
|
|
set_ic35recfld( rec, A_TelFax, StringValue( vprop, NULL ) );
|
|
else if ( strcasecmp( "Business", CategoryValue( vcard ) ) == 0 )
|
|
set_ic35recfld( rec, A_TelWork, StringValue( vprop, NULL ) );
|
|
else
|
|
set_ic35recfld( rec, A_TelHome, StringValue( vprop, NULL ) );
|
|
}
|
|
if ( strcasecmp( name, VCEmailAddressProp ) == 0 )
|
|
switch ( ++emailnum ) {
|
|
case 1:
|
|
set_ic35recfld( rec, A_Email1,
|
|
StringValue( vprop, NULL ) );
|
|
break;
|
|
case 2:
|
|
set_ic35recfld( rec, A_Email2,
|
|
StringValue( vprop, NULL ) );
|
|
break;
|
|
}
|
|
}
|
|
|
|
set_ic35recfld( rec, A_URL, StringValue( vcard, VCURLProp ) );
|
|
|
|
set_ic35recfld( rec, A_BirthDate,
|
|
isodtime_to_ymd( vcard, VCBirthDateProp ) );
|
|
|
|
set_ic35recfld( rec, A_Def1, NotePropValue( vcard, DEF1PROP ) );
|
|
set_ic35recfld( rec, A_Def2, NotePropValue( vcard, DEF2PROP ) );
|
|
set_ic35recfld( rec, A_Notes, NotesValue( vcard ) );
|
|
|
|
set_ic35recfld( rec, A_CategoryID, "\x00" );
|
|
set_ic35recfld( rec, A_Category, CategoryValue( vcard ) );
|
|
}
|
|
|
|
|
|
/* ==================================================== */
|
|
/* IC35 Schedule record to/from vCal:vEvent */
|
|
/* ==================================================== */
|
|
/*
|
|
* field mapping:
|
|
* VCSummaryProp S_Subject
|
|
* VCDTstartProp
|
|
* date S_StartDate
|
|
* time S_StartTime
|
|
* VCDTendProp
|
|
* date S_EndDate
|
|
* time S_EndTime
|
|
* VCRRuleProp
|
|
* type D,W,MP,MD,YD S_Alarm_Repeat & RepeatMASK
|
|
* D daily RepeatDay
|
|
* W weekly RepeatWeek
|
|
* MP monthly RepeatMonWday
|
|
* MD monthly RepeatMonMday
|
|
* YD yearly RepeatYear
|
|
* count n S_RepCount
|
|
* end-isodtime S_RepEndDate only yyyymmdd, max 20311231
|
|
* VCAAlarmProp S_Alarm_Repeat & AlarmNoBeep
|
|
* VCRunTimeProp S_AlarmBefore default: AlarmBefNone
|
|
* VCDAlarmProp S_Alarm_Repeat & AlarmNoLED
|
|
* VCRunTimeProp S_AlarmBefore default: AlarmBefNone
|
|
* VCRunTimeProp - VCDTstartProp
|
|
* < 60 AlarmBefNow
|
|
* >= 60 * 1 AlarmBef1min
|
|
* >= 60 * 5 AlarmBef5min
|
|
* >= 60 * 10 AlarmBef10min
|
|
* >= 60 * 30 AlarmBef30min
|
|
* >= 60 * 60 * 1 AlarmBef1hour
|
|
* >= 60 * 60 * 2 AlarmBef2hour
|
|
* >= 60 * 60 * 10 AlarmBef10hour
|
|
* >= 60 * 60 * 24 * 1 AlarmBef1day
|
|
* >= 60 * 60 * 24 * 2 AlarmBef2day
|
|
* VCDescriptionProp S_Notes
|
|
* -/- S_CategoryID default: 0x00
|
|
* VCCategoriesProp S_Category default: "Unfiled"
|
|
* use first known standard from multiple (Business,Personal,Unfiled)
|
|
* if single use it, even if non-standard
|
|
* special for korganizer:
|
|
* korganizer supports only DALARM
|
|
* special for gnomecal:
|
|
* VCSummaryProp
|
|
* first line S_Subject
|
|
* subsequent lines S_Notes
|
|
* gnomecal does not support VEVENT.DESCRIPTION, but multiple lines
|
|
* in VEVENT.SUMMARY, which in turn the IC35 does not support.
|
|
* as workaround the first line of VEVENT.SUMMARY is mapped to S_Subject,
|
|
* all subsequent lines are mapped to S_Notes in IC35.
|
|
* VCClassProp PUBLIC
|
|
* gnomecal crashes on edit if VCClassProp is not in VEVENT record
|
|
* gnomecal,korganizer automagically add:
|
|
* CLASS:PUBLIC gnomecal need korganizer
|
|
* PRIORITY:0 gnomecal korganizer
|
|
* RELATED-TO:0 - korganizer
|
|
* SEQUENCE:0 gnomecal korganizer
|
|
* STATUS:NEEDS ACTION gnomecal korganizer
|
|
* TRANSP:0 gnomecal korganizer
|
|
* X-ORGANIZER:MAILTO:tsch@shs5.ind-hh korganizer
|
|
*/
|
|
|
|
/* convert IC35 Schedule record to vCal:vEvent
|
|
* -------------------------------------------
|
|
*/
|
|
static void
|
|
ic35sched_to_vevent( IC35REC * rec, VObject * vevent )
|
|
{
|
|
VObject * vprop;
|
|
uchar alarm_repeat;
|
|
char isodtime[20];
|
|
char * notes;
|
|
|
|
sprintf( isodtime, "%sT%s", /* yyyymmddThhmmss */
|
|
ic35recfld( rec, S_StartDate ), ic35recfld( rec, S_StartTime ) );
|
|
if ( isodtime[13] == ':' ) isodtime[13] = isodtime[14] = '0';
|
|
SetString( vevent, VCDTstartProp, isodtime );
|
|
sprintf( isodtime, "%sT%s", /* yyyymmddThhmmss */
|
|
ic35recfld( rec, S_EndDate ), ic35recfld( rec, S_EndTime ) );
|
|
if ( isodtime[13] == '<' ) isodtime[13] = isodtime[14] = '0';
|
|
SetString( vevent, VCDTendProp, isodtime );
|
|
|
|
if ( _vca_prodid() == VCAL_GNOME ) {
|
|
if ( (notes = ic35recfld( rec, S_Notes )) && *notes ) {
|
|
char * subj = ic35recfld( rec, S_Subject );
|
|
char * buff = dupStr( "", strlen(subj)+2+strlen(notes) );
|
|
sprintf( buff, "%s\r\n%s", subj, notes );
|
|
subj = dupSubstCRLF( buff );
|
|
SetString( vevent, VCSummaryProp, subj );
|
|
deleteStr( subj );
|
|
deleteStr( buff );
|
|
} else
|
|
SetString( vevent, VCSummaryProp, ic35recfld( rec, S_Subject ) );
|
|
if ( ! isAPropertyOf( vevent, VCClassProp ) )
|
|
SetString( vevent, VCClassProp, "PUBLIC" );
|
|
} else {
|
|
SetString( vevent, VCSummaryProp, ic35recfld( rec, S_Subject ) );
|
|
SetNotes( vevent, ic35recfld( rec, S_Notes ) );
|
|
}
|
|
|
|
alarm_repeat = *ic35recfld( rec, S_Alarm_Repeat );
|
|
if ( alarm_repeat & RepeatMASK ) {
|
|
uchar repcount = *ic35recfld( rec, S_RepCount );
|
|
char * repend = ic35recfld( rec, S_RepEndDate );
|
|
char reprule[24];
|
|
struct tm stm;
|
|
static const char * wdays[] = {
|
|
"SU", "MO", "TU", "WE", "TH", "FR", "SA",
|
|
};
|
|
|
|
memset( &stm, 0, sizeof(stm) );
|
|
sscanf( ic35recfld( rec, S_StartDate ), "%4d%2d%2d",
|
|
&stm.tm_year, &stm.tm_mon, &stm.tm_mday );
|
|
sscanf( ic35recfld( rec, S_StartTime ), "%2d%2d",
|
|
&stm.tm_hour, &stm.tm_min );
|
|
stm.tm_year -= 1900;
|
|
stm.tm_mon -= 1;
|
|
stm.tm_sec = 0;
|
|
stm.tm_isdst = -1;
|
|
(void)mktime( &stm );
|
|
repcount = *ic35recfld( rec, S_RepCount );
|
|
repend = ic35recfld( rec, S_RepEndDate );
|
|
*reprule = '\0';
|
|
switch ( alarm_repeat & RepeatMASK ) {
|
|
case RepeatDay:
|
|
sprintf( reprule, "D%u %sT000000", repcount, repend );
|
|
break;
|
|
case RepeatWeek:
|
|
sprintf( reprule, "W%u %s %sT000000",
|
|
repcount, wdays[stm.tm_wday], repend );
|
|
break;
|
|
case RepeatMonWday:
|
|
sprintf( reprule, "MP%u %sT000000", repcount, repend );
|
|
break;
|
|
case RepeatMonMday:
|
|
sprintf( reprule, "MD%u %sT000000", repcount, repend );
|
|
break;
|
|
case RepeatYear:
|
|
sprintf( reprule, "YD%u %sT000000", repcount, repend );
|
|
break;
|
|
}
|
|
SetString( vevent, VCRRuleProp, reprule );
|
|
} else
|
|
DelProp( vevent, VCRRuleProp );
|
|
if ( *ic35recfld( rec, S_AlarmBefore )
|
|
&& (alarm_repeat & (AlarmNoBeep|AlarmNoLED))
|
|
!= (AlarmNoBeep|AlarmNoLED) ) {
|
|
time_t atime;
|
|
struct tm * ptm;
|
|
struct tm atm;
|
|
char atimestr[16];
|
|
|
|
memset( &atm, 0, sizeof(atm) );
|
|
sscanf( ic35recfld( rec, S_StartDate ), "%4d%2d%2d",
|
|
&atm.tm_year, &atm.tm_mon, &atm.tm_mday );
|
|
sscanf( ic35recfld( rec, S_StartTime ), "%2d%2d",
|
|
&atm.tm_hour, &atm.tm_min );
|
|
atm.tm_year -= 1900;
|
|
atm.tm_mon -= 1;
|
|
atm.tm_isdst = -1;
|
|
atime = mktime( &atm );
|
|
switch ( *ic35recfld( rec, S_AlarmBefore ) ) {
|
|
case AlarmBefNone:
|
|
case AlarmBefNow: break;
|
|
case AlarmBef1min: atime -= 60; break;
|
|
case AlarmBef5min: atime -= 5 * 60; break;
|
|
case AlarmBef10min: atime -= 10 * 60; break;
|
|
case AlarmBef30min: atime -= 30 * 60; break;
|
|
case AlarmBef1hour: atime -= 60 * 60; break;
|
|
case AlarmBef2hour: atime -= 2 * 60 * 60; break;
|
|
case AlarmBef10hour: atime -= 10 * 60 * 60; break;
|
|
case AlarmBef1day: atime -= 24 * 60 * 60; break;
|
|
case AlarmBef2day: atime -= 2 * 24 * 60 * 60; break;
|
|
}
|
|
ptm = localtime( &atime );
|
|
strftime( atimestr, sizeof(atimestr), "%Y%m%dT%H%M%S", ptm );
|
|
if ( ! (alarm_repeat & AlarmNoBeep) && _vca_prodid() != VCAL_KDE ) {
|
|
vprop = SetProp( vevent, VCAAlarmProp );
|
|
SetString( vprop, VCRunTimeProp, atimestr );
|
|
SetString( vprop, VCSnoozeTimeProp, "" );
|
|
SetString( vprop, VCRepeatCountProp, "1" );
|
|
} else
|
|
DelProp( vevent, VCAAlarmProp );
|
|
if ( ! (alarm_repeat & AlarmNoLED)
|
|
|| (!(alarm_repeat & AlarmNoBeep) && _vca_prodid() == VCAL_KDE) ) {
|
|
vprop = SetProp( vevent, VCDAlarmProp );
|
|
SetString( vprop, VCRunTimeProp, atimestr );
|
|
SetString( vprop, VCSnoozeTimeProp, "" );
|
|
SetString( vprop, VCRepeatCountProp, "1" );
|
|
} else
|
|
DelProp( vevent, VCDAlarmProp );
|
|
} else {
|
|
DelProp( vevent, VCAAlarmProp );
|
|
DelProp( vevent, VCDAlarmProp );
|
|
}
|
|
}
|
|
|
|
/* convert IC35 schedule record to vCal
|
|
* ------------------------------------
|
|
*/
|
|
static void
|
|
vevent_to_ic35sched( VObject * vevent, IC35REC * rec )
|
|
{
|
|
VObject * vprop;
|
|
char * strval;
|
|
char * pstr;
|
|
char alarm_repeat[2];
|
|
char repeat_count[2];
|
|
char alarm_before[2];
|
|
time_t abefsec;
|
|
|
|
strval = StringValue( vevent, VCSummaryProp );
|
|
if ( !( strval && *strval ) ) strval = "NoSubject"; /*IC35 needs subject*/
|
|
if ( _vca_prodid() == VCAL_GNOME
|
|
&& (pstr = strchr( strval, '\n' )) != NULL ) {
|
|
strval = dupStr( strval, pstr - strval ); /* first line */
|
|
set_ic35recfld( rec, S_Subject, strval ); /* to Subject field */
|
|
deleteStr( strval );
|
|
pstr = dupSubstNL( pstr+1 ); /* subsequent lines */
|
|
set_ic35recfld( rec, S_Notes, pstr ); /* to Notes field */
|
|
deleteStr( pstr );
|
|
} else {
|
|
set_ic35recfld( rec, S_Subject, strval );
|
|
set_ic35recfld( rec, S_Notes, NotesValue( vevent ) );
|
|
}
|
|
|
|
set_ic35recfld( rec, S_StartDate,
|
|
isodtime_to_ymd_or_today( vevent, VCDTstartProp ) );
|
|
set_ic35recfld( rec, S_StartTime,
|
|
isodtime_to_hms_or_now( vevent, VCDTstartProp ) );
|
|
set_ic35recfld( rec, S_EndDate,
|
|
isodtime_to_ymd_or_today( vevent, VCDTendProp ) );
|
|
set_ic35recfld( rec, S_EndTime,
|
|
isodtime_to_hms_or_now( vevent, VCDTendProp ) );
|
|
|
|
alarm_repeat[0] = 0;
|
|
alarm_repeat[1] = '\0';
|
|
repeat_count[0] = 0;
|
|
repeat_count[1] = '\0';
|
|
if ( (strval = StringValue( vevent, VCRRuleProp )) && *strval ) {
|
|
if ( strncasecmp( strval, "YD", 2 ) == 0 ) {
|
|
alarm_repeat[0] = (alarm_repeat[0] & RepeatMASK) | RepeatYear;
|
|
repeat_count[0] = (uchar)atoi( strval+2 );
|
|
} else if ( strncasecmp( strval, "MD", 2 ) == 0 ) {
|
|
alarm_repeat[0] = (alarm_repeat[0] & RepeatMASK) | RepeatMonMday;
|
|
repeat_count[0] = (uchar)atoi( strval+2 );
|
|
} else if ( strncasecmp( strval, "MP", 2 ) == 0 ) {
|
|
alarm_repeat[0] = (alarm_repeat[0] & RepeatMASK) | RepeatMonWday;
|
|
repeat_count[0] = (uchar)atoi( strval+2 );
|
|
} else if ( strncasecmp( strval, "W", 1 ) == 0 ) {
|
|
alarm_repeat[0] = (alarm_repeat[0] & RepeatMASK) | RepeatWeek;
|
|
repeat_count[0] = (uchar)atoi( strval+1 );
|
|
} else if ( strncasecmp( strval, "D", 1 ) == 0 ) {
|
|
alarm_repeat[0] = (alarm_repeat[0] & RepeatMASK) | RepeatDay;
|
|
repeat_count[0] = (uchar)atoi( strval+1 );
|
|
}
|
|
if ( (pstr = strrchr( strval, ' ' )) != NULL
|
|
&& strlen( pstr+1 ) >= 8 /* yyyymmdd */
|
|
&& isdigit( pstr[1] ) )
|
|
set_ic35recfld( rec, S_RepEndDate,
|
|
isodtstr_to_ymd_or_today( pstr+1 ) );
|
|
}
|
|
|
|
alarm_repeat[0] |= (AlarmNoBeep|AlarmNoLED);
|
|
alarm_before[0] = AlarmBefNone;
|
|
alarm_before[1] = '\0';
|
|
abefsec = 0;
|
|
if ( (vprop = isAPropertyOf( vevent, VCAAlarmProp )) != NULL ) {
|
|
alarm_repeat[0] &= ~AlarmNoBeep;
|
|
abefsec = isodtime_to_unixtime( vevent, VCDTstartProp )
|
|
- isodtime_to_unixtime( vprop, VCRunTimeProp );
|
|
}
|
|
if ( (vprop = isAPropertyOf( vevent, VCDAlarmProp )) != NULL ) {
|
|
alarm_repeat[0] &= ~AlarmNoLED;
|
|
if ( _vca_prodid() == VCAL_KDE ) /* korganizer has only DALARM */
|
|
alarm_repeat[0] &= ~AlarmNoBeep;
|
|
abefsec = isodtime_to_unixtime( vevent, VCDTstartProp )
|
|
- isodtime_to_unixtime( vprop, VCRunTimeProp );
|
|
}
|
|
if ( (alarm_repeat[0] & (AlarmNoBeep|AlarmNoLED))
|
|
!= (AlarmNoBeep|AlarmNoLED) ) {
|
|
if ( abefsec < 60 ) alarm_before[0] = AlarmBefNow;
|
|
if ( abefsec >= 60 * 1 ) alarm_before[0] = AlarmBef1min;
|
|
if ( abefsec >= 60 * 5 ) alarm_before[0] = AlarmBef5min;
|
|
if ( abefsec >= 60 * 10 ) alarm_before[0] = AlarmBef10min;
|
|
if ( abefsec >= 60 * 30 ) alarm_before[0] = AlarmBef30min;
|
|
if ( abefsec >= 60 * 60 * 1 ) alarm_before[0] = AlarmBef1hour;
|
|
if ( abefsec >= 60 * 60 * 2 ) alarm_before[0] = AlarmBef2hour;
|
|
if ( abefsec >= 60 * 60 * 10 ) alarm_before[0] = AlarmBef10hour;
|
|
if ( abefsec >= 60 * 60 * 24 * 1 ) alarm_before[0] = AlarmBef1day;
|
|
if ( abefsec >= 60 * 60 * 24 * 2 ) alarm_before[0] = AlarmBef2day;
|
|
}
|
|
set_ic35recfld( rec, S_Alarm_Repeat, alarm_repeat );
|
|
set_ic35recfld( rec, S_RepCount, repeat_count );
|
|
set_ic35recfld( rec, S_AlarmBefore, alarm_before );
|
|
|
|
/* IC35 Schedule does not have Category */
|
|
}
|
|
|
|
|
|
/* ==================================================== */
|
|
/* IC35 ToDoList record to/from vCal:vTodo */
|
|
/* ==================================================== */
|
|
/*
|
|
* field mapping:
|
|
* VCSummaryProp T_Subject default: "ToDo"
|
|
* VCDTstartProp T_StartDate yyyymmdd default: today
|
|
* VCDueProp T_EndDate yyyymmdd default: today
|
|
* VCStatusProp T_Completed 1 byte default: 0x00
|
|
* NEEDS ACTION 0x00
|
|
* COMPLETED 0x01
|
|
* other 0x00 "STATUS:<stat>\r\n" to T_Notes
|
|
* VCPriorityProp T_Priority 1 byte default: 0x01
|
|
* 1,2 0x02 highest
|
|
* 3,4,0 0x01 normal
|
|
* 5.. 0x00 lowest
|
|
* VCDescriptionProp T_Notes default: <empty>
|
|
* -/- T_CategoryID default: 0x00
|
|
* VCCategoriesProp T_Category default: "Unfiled"
|
|
* use first known standard from multiple (Business,Personal,Unfiled)
|
|
* if single use it, even if non-standard
|
|
* special for KOrganizer:
|
|
* VCDescriptionProp T_Notes
|
|
* "DTSTART:<cont>\r\n" T_StartDate
|
|
* "DUE:<cont>\r\n" T_EndDate
|
|
* "CATEGORIES:<cont>\r\n" T_Category
|
|
* KOrganizer does not support VTODO.DTSTART,DUE,CATEGORIES,
|
|
* as workaround these fields are put into VTODO.DESCRIPTION.
|
|
* special for gnomecal:
|
|
* VCDescriptionProp T_Notes
|
|
* "DTSTART:<cont>\r\n" T_StartDate
|
|
* gnomecal does not support VTODO.DTSTART, put into VTODO.DESCRIPTION.
|
|
* VCClassProp PUBLIC
|
|
* gnomecal crashes on edit if VCClassProp is not in VTODO record
|
|
* gnomecal,korganizer automagically add:
|
|
* CLASS:PUBLIC gnomecal need -
|
|
* SEQUENCE:0 gnomecal korganizer
|
|
* TRANSP:0 gnomecal -
|
|
* X-ORGANIZER:MAILTO:tsch@shs5.ind-hh korganizer
|
|
*/
|
|
|
|
/* convert IC35 ToDoList record to vCal:vTodo
|
|
* ------------------------------------------
|
|
*/
|
|
static void
|
|
ic35todo_to_vtodo( IC35REC * rec, VObject * vtodo )
|
|
{
|
|
char * prio;
|
|
char isodtime[20];
|
|
|
|
SetString( vtodo, VCSummaryProp, ic35recfld( rec, T_Subject ) );
|
|
SetNotes( vtodo, ic35recfld( rec, T_Notes ) );
|
|
|
|
switch ( _vca_prodid() ) {
|
|
case VCAL_KDE: /* does not support DTSTART,DUE,CATEGORY */
|
|
SetNoteProp( vtodo, VCDueProp, ic35recfld( rec, T_EndDate ) );
|
|
SetNoteProp( vtodo, VCDTstartProp, ic35recfld( rec, T_StartDate ) );
|
|
SetNoteProp( vtodo, VCCategoriesProp, ic35recfld( rec, T_Category ) );
|
|
break;
|
|
case VCAL_GNOME: /* does not support DTSTART, needs CLASS */
|
|
if ( ! isAPropertyOf( vtodo, VCClassProp ) )
|
|
SetString( vtodo, VCClassProp, "PUBLIC" );
|
|
SetNoteProp( vtodo, VCDTstartProp, ic35recfld( rec, T_StartDate ) );
|
|
goto due_category;
|
|
default:
|
|
sprintf( isodtime, "%sT000000", ic35recfld( rec, T_StartDate ) );
|
|
SetString( vtodo, VCDTstartProp, isodtime );
|
|
due_category:
|
|
sprintf( isodtime, "%sT235900", ic35recfld( rec, T_EndDate ) );
|
|
SetString( vtodo, VCDueProp, isodtime );
|
|
SetCategory( vtodo, ic35recfld( rec, T_Category ) );
|
|
}
|
|
|
|
SetString( vtodo, VCStatusProp, *ic35recfld( rec, T_Completed ) == 0
|
|
? "NEEDS ACTION" : "COMPLETED" );
|
|
|
|
switch ( *ic35recfld( rec, T_Priority ) ) {
|
|
case 0x02: prio = "1"; break; /* highest */
|
|
default:
|
|
case 0x01: prio = "3"; break; /* normal */
|
|
case 0x00: prio = "5"; break; /* lowest */
|
|
}
|
|
SetString( vtodo, VCPriorityProp, prio );
|
|
}
|
|
|
|
/* convert IC35 ToDoList record to vCal
|
|
* ------------------------------------
|
|
*/
|
|
static void
|
|
vtodo_to_ic35todo( VObject * vtodo, IC35REC * rec )
|
|
{
|
|
char *binstr, *strval;
|
|
int intval;
|
|
char * statusnote = NULL;
|
|
char * dtbeg;
|
|
char * dtend;
|
|
|
|
strval = StringValue( vtodo, VCSummaryProp );
|
|
if ( !( strval && *strval ) ) strval = "NoSubject"; /*IC35 needs subject*/
|
|
set_ic35recfld( rec, T_Subject, strval );
|
|
|
|
switch ( _vca_prodid() ) {
|
|
case VCAL_KDE: /* does not support DTSTART,DUE,CATEGORY */
|
|
dtbeg = dupStr( NotePropValue( vtodo, VCDTstartProp ), 0 );
|
|
dtend = dupStr( NotePropValue( vtodo, VCDueProp ), 0 );
|
|
set_ic35recfld( rec, T_Category,
|
|
NotePropValue( vtodo, VCCategoriesProp ) );
|
|
break;
|
|
case VCAL_GNOME: /* does not support DTSTART */
|
|
dtbeg = dupStr( NotePropValue( vtodo, VCDTstartProp ), 0 );
|
|
goto due_category;
|
|
default:
|
|
dtbeg = dupStr( StringValue( vtodo, VCDTstartProp ), 0 );
|
|
due_category:
|
|
dtend = dupStr( StringValue( vtodo, VCDueProp ), 0 );
|
|
set_ic35recfld( rec, T_Category,
|
|
CategoryValue( vtodo ) );
|
|
}
|
|
set_ic35recfld( rec, T_CategoryID, "\x00" );
|
|
set_ic35recfld( rec, T_StartDate, isodtstr_to_ymd_or_today( dtbeg ) );
|
|
set_ic35recfld( rec, T_EndDate, isodtstr_to_ymd_or_today( dtend ) );
|
|
deleteStr( dtbeg );
|
|
deleteStr( dtend );
|
|
|
|
binstr = "\x00";
|
|
if ( (strval = StringValue( vtodo, VCStatusProp )) && *strval ) {
|
|
if ( strcasecmp( strval, "COMPLETED" ) == 0 )
|
|
binstr = "\x01";
|
|
else if ( strcasecmp( strval, "NEEDS ACTION" ) != 0
|
|
&& (statusnote = malloc( 255+1 )) != NULL ) {
|
|
strcpy( statusnote, "STATUS:" );
|
|
strncat( strval, strval, 255-7-2 );
|
|
strcat( strval, "\r\n" );
|
|
}
|
|
}
|
|
set_ic35recfld( rec, T_Completed, binstr );
|
|
|
|
intval = atoi( StringValue( vtodo, VCPriorityProp ) );
|
|
if ( intval >= 5 ) binstr = "\x00"; /* lowest */
|
|
else if ( intval >= 3 ) binstr = "\x01"; /* normal */
|
|
else if ( intval >= 1 ) binstr = "\x02"; /* highest */
|
|
else /* intval == 0 */ binstr = "\x01"; /* default: normal */
|
|
set_ic35recfld( rec, T_Priority, binstr );
|
|
|
|
if ( statusnote ) {
|
|
strval = NotesValue( vtodo );
|
|
strncat( statusnote, strval, 255 - strlen( statusnote ) );
|
|
set_ic35recfld( rec, T_Notes, statusnote );
|
|
free( statusnote );
|
|
} else
|
|
set_ic35recfld( rec, T_Notes, NotesValue( vtodo ) );
|
|
}
|
|
|
|
|
|
/* ==================================================== */
|
|
/* IC35 Memo record to/from vMemo (proprietary) */
|
|
/* ==================================================== */
|
|
/*
|
|
* field mapping:
|
|
* VCSummaryProp M_Subject default: "Memo"
|
|
* VCNoteProp M_Notes
|
|
* -/- M_CategoryID default: 0x00
|
|
* VCCategoriesProp M_Category default: "Unfiled"
|
|
* use first known standard from multiple (Business,Personal,Unfiled)
|
|
* if single use it, even if non-standard
|
|
*/
|
|
|
|
/* convert IC35 Memo record to (proprietary) vMemo
|
|
* -----------------------------------------------
|
|
*/
|
|
static void
|
|
ic35memo_to_vmemo( IC35REC * rec, VObject * vmemo )
|
|
{
|
|
SetString( vmemo, VCSummaryProp, ic35recfld( rec, M_Subject ) );
|
|
SetNotes( vmemo, ic35recfld( rec, M_Notes ) );
|
|
SetCategory( vmemo, ic35recfld( rec, M_Category ) );
|
|
}
|
|
|
|
/* convert IC35 Memo record to (proprietary) vMemo
|
|
* -----------------------------------------------
|
|
*/
|
|
static void
|
|
vmemo_to_ic35memo( VObject * vmemo, IC35REC * rec )
|
|
{
|
|
char *strval;
|
|
|
|
strval = StringValue( vmemo, VCSummaryProp );
|
|
if ( !( strval && *strval ) ) strval = "NoSubject"; /*IC35 needs subject*/
|
|
set_ic35recfld( rec, M_Subject, strval );
|
|
|
|
set_ic35recfld( rec, M_Notes, NotesValue( vmemo ) );
|
|
|
|
set_ic35recfld( rec, M_CategoryID, "\x00" );
|
|
set_ic35recfld( rec, M_Category, CategoryValue( vmemo ) );
|
|
}
|
|
|
|
|
|
/* ============================================ */
|
|
/* vCard,vCal access functions */
|
|
/* ============================================ */
|
|
/*
|
|
* vca_open()
|
|
* vca_close()
|
|
* vca_rewind()
|
|
*
|
|
* vca_getrec( int fileid ) -> VObject* / NULL
|
|
* vca_getrec_byID( ulong recid ) -> VObject* / NULL
|
|
* vca_cmpic35rec( IC35REC*, VObject* ) -> 0:same 1:differ
|
|
* vca_updic35rec( IC35REC*, VObject* ) VObject to IC35REC
|
|
* vca_putic35rec( IC35REC* ) IC35REC to VObject
|
|
* vca_delrec( VObject* )
|
|
*
|
|
* vca_recid( VObject* )
|
|
* vca_set_recid( VObject*, ulong )
|
|
* vca_recstat( VObject* )
|
|
* vca_set_recstat( VObject*, int )
|
|
*/
|
|
|
|
struct vcaget { /* current position in vcalist */
|
|
VObject * vobj; /* current vCard,vCal,vMemo */
|
|
bool in_vcal; /* flag: vcal_iterator in use */
|
|
VObjectIterator vcal_iter; /* iterator within vCal object */
|
|
};
|
|
|
|
static VObject * vcalist; /* list of vCard,vCal,vMemo objects */
|
|
static VObject * vcal; /* vCalendar contains vEvent,vTodo */
|
|
static struct vcaget vcaget; /* _vca_getrec position in vcalist */
|
|
|
|
static ulong vca_recid( VObject * vobj );
|
|
|
|
/* internal: determine vCalender program from PRODID
|
|
* -------------------------------------------------
|
|
* inspect vCalender PRODID for creating program:
|
|
* PRODID:-//GNOME//NONSGML GnomeCalendar//EN
|
|
* PRODID:-//K Desktop Environment//NONSGML KOrganizer//EN
|
|
* some IC35 to/from vCalendar conversions are handled special
|
|
* depending on program.
|
|
*/
|
|
static int
|
|
_vca_prodid( void )
|
|
{
|
|
char * prodid;
|
|
int rval;
|
|
|
|
rval = 0;
|
|
if ( vcal != NULL
|
|
&& (prodid = dupStringValue( vcal, VCProdIdProp )) != NULL ) {
|
|
if ( strstr( prodid, "GnomeCalendar//" ) != NULL )
|
|
rval = VCAL_GNOME;
|
|
if ( strstr( prodid, "KOrganizer//" ) != NULL )
|
|
rval = VCAL_KDE;
|
|
deleteStr( prodid );
|
|
}
|
|
return rval;
|
|
}
|
|
|
|
/* internal: rewind vCard,vCal list
|
|
* --------------------------------
|
|
*/
|
|
static void
|
|
_vca_rewind( struct vcaget * vcaget )
|
|
{
|
|
vcaget->vobj = NULL;
|
|
vcaget->in_vcal = FALSE;
|
|
}
|
|
/* internal: get next vCard,vCal record
|
|
* ------------------------------------
|
|
*/
|
|
static VObject *
|
|
_vca_getnext( struct vcaget * vcaget )
|
|
{
|
|
VObject * vcal;
|
|
|
|
if ( vcaget == NULL )
|
|
return NULL;
|
|
if ( vcaget->vobj == NULL )
|
|
vcaget->vobj = vcalist;
|
|
else if ( ! vcaget->in_vcal )
|
|
vcaget->vobj = nextVObjectInList( vcaget->vobj );
|
|
while ( vcaget->vobj != NULL ) {
|
|
switch ( vca_type( vcaget->vobj ) ) {
|
|
case VCARD:
|
|
case VMEMO:
|
|
return vcaget->vobj;
|
|
case VCAL:
|
|
if ( ! vcaget->in_vcal ) {
|
|
initPropIterator( &vcaget->vcal_iter, vcaget->vobj );
|
|
vcaget->in_vcal = TRUE;
|
|
}
|
|
while ( moreIteration( &vcaget->vcal_iter ) ) {
|
|
vcal = nextVObject( &vcaget->vcal_iter );
|
|
switch ( vca_type( vcal ) ) {
|
|
case VEVENT:
|
|
case VTODO:
|
|
return vcal;
|
|
}
|
|
}
|
|
vcaget->in_vcal = FALSE;
|
|
break;
|
|
}
|
|
vcaget->vobj = nextVObjectInList( vcaget->vobj );
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* open vCard,vCal file(s)
|
|
* -----------------------
|
|
* if file(s) exist, read into internal vObject-tree 'vcalist'
|
|
* complain if open file(s) failed.
|
|
* ??? check if output file creatable in dirname(filename)
|
|
* rewind so that _vca_getrec() gets first record in 'vcalist'
|
|
*/
|
|
static char * iomode;
|
|
static char * addr_fname;
|
|
static char * vcal_fname;
|
|
static char * memo_fname;
|
|
static int addr_CRLF; /* CRLF / NL mode per PIMfile */
|
|
static int vcal_CRLF; /* from Parse_MIME_FromFile() */
|
|
static int memo_CRLF; /* calls in vca_open() */
|
|
|
|
/* mimeErrorHandler for Parse_MIME_FromFile()
|
|
*/
|
|
static int _vca_errok;
|
|
static char * _vca_fname;
|
|
|
|
static void
|
|
_vca_errmsg( char * msg )
|
|
{
|
|
error( "vCard/vCal \"%s\": %s", _vca_fname ? _vca_fname : "", msg );
|
|
_vca_errok = ERR;
|
|
}
|
|
|
|
/* check and read vCard/vCal from filepointer
|
|
*/
|
|
static int
|
|
_vca_readfp( FILE * fp, char * fname, VObject ** pvlist, int * pcrlf )
|
|
{
|
|
int chr;
|
|
|
|
LPRINTF(( L_INFO, "vca_open: read %s ..", fname ));
|
|
*pvlist = NULL;
|
|
*pcrlf = 1;
|
|
do {
|
|
if ( (chr = fgetc( fp )) == EOF ) /* empty file or only CR/LF */
|
|
return OK; /* is accepted OK */
|
|
} while ( chr == '\r' || chr == '\n' );
|
|
rewind( fp );
|
|
|
|
registerMimeErrorHandler( _vca_errmsg );
|
|
_vca_errok = OK;
|
|
_vca_fname = fname;
|
|
*pvlist = Parse_MIME_FromFile( fp );
|
|
*pcrlf = CRLFmode(); /* CRLF/NL input from Parse_MIME */
|
|
return _vca_errok; /* ok / error from _vca_errmsg() */
|
|
}
|
|
|
|
/* open,check+read,close vCard/vCal file
|
|
*/
|
|
static int
|
|
_vca_readfile( char * fname, VObject ** pvlist, int * pcrlf )
|
|
{
|
|
FILE * fp;
|
|
int rval;
|
|
|
|
*pvlist = NULL;
|
|
*pcrlf = 1;
|
|
if ( !( fname && *fname ) ) /* no filename specified */
|
|
return OK;
|
|
|
|
if ( (fp = fopen( fname, "r" )) == NULL ) {
|
|
if ( access( fname, F_OK ) == 0 )
|
|
return ERR; /* file exists, but not readable */
|
|
else
|
|
return OK; /* non-existing file is OK */
|
|
}
|
|
rval = _vca_readfp( fp, fname, pvlist, pcrlf );
|
|
fclose( fp );
|
|
return rval;
|
|
}
|
|
|
|
/* read-in vCard,vCal file(s)
|
|
*/
|
|
static int
|
|
vca_open( char * mode, char * addrfname, char * vcalfname, char * memofname )
|
|
{
|
|
VObject * vlist;
|
|
VObject * vobj;
|
|
int rval;
|
|
|
|
LPRINTF(( L_INFO, "vca_open(%s,%s,%s)",
|
|
addrfname ? addrfname : "NULL",
|
|
vcalfname ? addrfname : "NULL",
|
|
vcalfname ? addrfname : "NULL" ));
|
|
iomode = mode; /* note input/output for close */
|
|
addr_fname = addrfname; /* note filenames for close */
|
|
vcal_fname = vcalfname;
|
|
memo_fname = memofname;
|
|
vcalist = vcal = NULL; /* init vCard,vCal record lists */
|
|
addr_CRLF = vcal_CRLF = memo_CRLF = 1; /* init CRLF mode default ON */
|
|
if ( iomode[0] != 'r' )
|
|
return OK; /* open for output only */
|
|
|
|
if ( addrfname && strcmp( addrfname, "-" ) == 0 /* using stdin */
|
|
&& !( vcalfname && *vcalfname ) /* is allowed if */
|
|
&& !( memofname && *memofname ) ) /* only one filename */
|
|
rval = _vca_readfp( stdin, "(stdin)", &vcalist, &addr_CRLF );
|
|
else
|
|
rval = _vca_readfile( addrfname, &vcalist, &addr_CRLF );
|
|
if ( rval != OK ) /* open/parse vCard file failed */
|
|
return ERR;
|
|
|
|
rval = _vca_readfile( vcalfname, &vcal, &vcal_CRLF );
|
|
if ( rval != OK ) /* open/parse vCal file failed */
|
|
return ERR;
|
|
if ( vcal != NULL ) /* vCal object parsed from file */
|
|
addList( &vcalist, vcal ); /* add vCal object to vcalist */
|
|
else /* vCal object in addr file */
|
|
for ( vobj = vcalist; vobj != NULL; vobj = nextVObjectInList( vobj ) )
|
|
if ( vca_type( vobj ) == VCAL ) {
|
|
vcal = vobj; /* setup pointer to vCal obj */
|
|
break;
|
|
}
|
|
|
|
rval = _vca_readfile( memofname, &vlist, &memo_CRLF );
|
|
if ( rval != OK ) /* open/parse VMEMO file failed */
|
|
return ERR;
|
|
while ( vlist != NULL ) {
|
|
vobj = vlist;
|
|
vlist = nextVObjectInList( vlist );
|
|
addList( &vcalist, vobj ); /* add memo records to vcalist */
|
|
}
|
|
|
|
_vca_rewind( &vcaget ); /* rewind for _vca_getrec() get first record */
|
|
LPRINTF(( L_INFO, "vca_open: done" ));
|
|
return OK;
|
|
}
|
|
|
|
/* close vCard,vCal file(s)
|
|
* ------------------------
|
|
* flush internal vObject-tree to vCard,vCal file(s)
|
|
* if file(s) exist, rename file(s) for backup
|
|
* static filename(s) were setup by pim_open()
|
|
*/
|
|
static void
|
|
vca_close( void )
|
|
{
|
|
FILE * addrfp = NULL;
|
|
FILE * vcalfp = NULL;
|
|
FILE * memofp = NULL;
|
|
VObject * vobj;
|
|
|
|
if ( iomode[0] == 'w' || iomode[1] == '+' ) {
|
|
addrfp = backup_and_openwr( addr_fname );
|
|
vcalfp = backup_and_openwr( vcal_fname );
|
|
if ( vcalfp == NULL ) { vcalfp = addrfp; vcal_CRLF = addr_CRLF; }
|
|
memofp = backup_and_openwr( memo_fname );
|
|
if ( memofp == NULL ) { memofp = vcalfp; memo_CRLF = memo_CRLF; }
|
|
}
|
|
LPRINTF(( L_INFO, "vca_close: %s",
|
|
addrfp && vcalist ? "write .." : "no output" ));
|
|
for ( vobj = vcalist; vobj != NULL; vobj = nextVObjectInList( vobj ) ) {
|
|
FILE * fp = NULL;
|
|
int crlf = 0;
|
|
|
|
switch ( vca_type( vobj ) ) {
|
|
case VCARD: fp = addrfp; crlf = addr_CRLF; break;
|
|
case VCAL: fp = vcalfp; crlf = vcal_CRLF; break;
|
|
case VMEMO: fp = memofp; crlf = memo_CRLF; break;
|
|
}
|
|
if ( fp != NULL ) {
|
|
setCRLFmode( crlf );
|
|
writeVObject( fp, vobj );
|
|
}
|
|
}
|
|
cleanVObjects( vcalist );
|
|
vcalist = vcal = NULL; /* all vCard,vCal,vMemo objects were removed */
|
|
_vca_rewind( &vcaget ); /* sanity for vca_getrec() */
|
|
|
|
if ( addrfp != NULL && addrfp != stdout )
|
|
fclose( addrfp );
|
|
if ( vcalfp && vcalfp != addrfp && vcalfp != stdout )
|
|
fclose( vcalfp );
|
|
if ( memofp && memofp != vcalfp && memofp != stdout )
|
|
fclose( memofp );
|
|
LPRINTF(( L_INFO, "vca_close: done" ));
|
|
}
|
|
|
|
/* rewind vCard,vCal list
|
|
* ----------------------
|
|
*/
|
|
static void
|
|
vca_rewind( void )
|
|
{
|
|
_vca_rewind( &vcaget );
|
|
}
|
|
|
|
/* get vCard,vCal record for fileid
|
|
* --------------------------------
|
|
* fileid specifies which type of record to get:
|
|
* FILEADDR VCARD
|
|
* FILESCHED VCAL:VEVENT
|
|
* FILETODO VCAL:VTODO
|
|
* FILEMEMO VMEMO
|
|
* returns:
|
|
* VObject* next record
|
|
* NULL no more records
|
|
*/
|
|
static VObject *
|
|
vca_getrec( int fileid )
|
|
{
|
|
VObject * vobj;
|
|
int wanttype;
|
|
|
|
switch ( fileid ) {
|
|
case FILEADDR: wanttype = VCARD; break;
|
|
case FILESCHED: wanttype = VEVENT; break;
|
|
case FILETODO: wanttype = VTODO; break;
|
|
case FILEMEMO: wanttype = VMEMO; break;
|
|
case FILE_ANY: wanttype = 0; break;
|
|
default:
|
|
return NULL; /* unknown fileid */
|
|
}
|
|
while ( (vobj = _vca_getnext( &vcaget )) ) {
|
|
if ( wanttype == 0 /* next record with any fileid */
|
|
|| vca_type( vobj ) == wanttype )
|
|
return vobj;
|
|
}
|
|
return NULL;
|
|
}
|
|
/* get vCard,vCal record by record-ID
|
|
* ----------------------------------
|
|
*/
|
|
static VObject *
|
|
vca_getrec_byID( ulong recid )
|
|
{
|
|
VObject * vobj;
|
|
struct vcaget tmpget;
|
|
|
|
_vca_rewind( &tmpget );
|
|
while ( (vobj = _vca_getnext( &tmpget )) )
|
|
if ( vca_recid( vobj ) == recid )
|
|
return vobj;
|
|
return NULL;
|
|
}
|
|
|
|
/* update IC35 record with vCard,vCal record
|
|
* -----------------------------------------
|
|
*/
|
|
static IC35REC *
|
|
vca_updic35rec( IC35REC * ic35rec, VObject * vobj )
|
|
{
|
|
int fid;
|
|
void (* vobj_to_ic35 )(VObject*,IC35REC*);
|
|
|
|
switch ( vca_type( vobj ) ) {
|
|
case VCARD: vobj_to_ic35 = vcard_to_ic35addr; fid = FILEADDR; break;
|
|
case VEVENT: vobj_to_ic35 = vevent_to_ic35sched; fid = FILESCHED; break;
|
|
case VTODO: vobj_to_ic35 = vtodo_to_ic35todo; fid = FILETODO; break;
|
|
case VMEMO: vobj_to_ic35 = vmemo_to_ic35memo; fid = FILEMEMO; break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
if ( ic35rec == NULL
|
|
&& (ic35rec = new_ic35rec()) == NULL )
|
|
return NULL;
|
|
/* must set fileid,recid first to specify number of fields */
|
|
set_ic35recid( ic35rec, FileRecId( fid, RecId(vca_recid( vobj )) ) );
|
|
(*vobj_to_ic35)( vobj, ic35rec );
|
|
return ic35rec;
|
|
}
|
|
|
|
/* compare IC35 record with vCard,vCal record
|
|
* ------------------------------------------
|
|
*/
|
|
static int
|
|
vca_cmpic35rec( IC35REC * ic35rec, VObject * vobj )
|
|
{
|
|
IC35REC * pimic35;
|
|
int rval;
|
|
|
|
if ( ic35rec == NULL && vobj == NULL )
|
|
return 0;
|
|
if ( ic35rec == NULL || vobj == NULL )
|
|
return -1;
|
|
|
|
if ( (pimic35 = new_ic35rec()) == NULL )
|
|
return -1;
|
|
vca_updic35rec( pimic35, vobj );
|
|
rval = cmp_ic35rec( ic35rec, pimic35 );
|
|
del_ic35rec( pimic35 );
|
|
return rval;
|
|
}
|
|
|
|
/* put IC35 record to (new) vCard,vCal record
|
|
* ------------------------------------------
|
|
*/
|
|
static VObject *
|
|
vca_putic35rec( IC35REC * ic35rec )
|
|
{
|
|
VObject * oldvobj;
|
|
VObject * vobj = NULL;
|
|
VObject * locvcal;
|
|
const char *id;
|
|
void (* ic35_to_vobj )(IC35REC*,VObject*);
|
|
|
|
switch ( FileId( ic35recid( ic35rec ) ) ) {
|
|
case FILEADDR: ic35_to_vobj = ic35addr_to_vcard; id = VCCardProp; break;
|
|
case FILEMEMO: ic35_to_vobj = ic35memo_to_vmemo; id = VCMemoProp; break;
|
|
case FILESCHED: ic35_to_vobj = ic35sched_to_vevent; id = VCEventProp; break;
|
|
case FILETODO: ic35_to_vobj = ic35todo_to_vtodo; id = VCTodoProp; break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
if ( (vobj = oldvobj = vca_getrec_byID( ic35recid( ic35rec ) )) == NULL
|
|
&& (vobj = newVObject( id )) == NULL )
|
|
return NULL;
|
|
clr_vobjdirty();
|
|
(*ic35_to_vobj)( ic35rec, vobj );
|
|
SetModtimeIfdirty( vobj );
|
|
vca_set_recid( vobj, ic35recid( ic35rec ) );
|
|
vca_set_recstat( vobj, VCA_CLEAN );
|
|
if ( oldvobj != NULL )
|
|
return vobj;
|
|
|
|
switch ( vca_type( vobj ) ) {
|
|
case VCARD:
|
|
case VMEMO:
|
|
addList( &vcalist, vobj );
|
|
break;
|
|
case VEVENT:
|
|
case VTODO:
|
|
/* create vCalendar header of not yet present */
|
|
if ( (locvcal = vcal) == NULL )
|
|
locvcal = newVObject( VCCalProp );
|
|
if ( isAPropertyOf( locvcal, VCProdIdProp ) == NULL )
|
|
addPropValue( locvcal, VCProdIdProp,
|
|
"-//IC35//NONSGML IC35Calendar//EN" );
|
|
/* -//GNOME//NONSGML GnomeCalendar//EN */
|
|
/* -//K Desktop Environment//NONSGML KOrganizer//EN */
|
|
if ( isAPropertyOf( locvcal, VCVersionProp ) == NULL )
|
|
addPropValue( locvcal, VCVersionProp, "1.0" );
|
|
if ( isAPropertyOf( locvcal, VCTimeZoneProp ) == NULL
|
|
|| isAPropertyOf( locvcal, VCDCreatedProp ) == NULL ) {
|
|
extern long timezone;
|
|
time_t tnow;
|
|
struct tm * ptm;
|
|
char isodtime[24];
|
|
char tzoffset[8];
|
|
long tzmineastUTC;
|
|
tnow = time( NULL ); ptm = localtime( &tnow );
|
|
tzmineastUTC = -timezone / 60;
|
|
strftime( isodtime, sizeof(isodtime), "%Y-%m-%dT%T", ptm );
|
|
sprintf( tzoffset, "%+03ld:%02ld",
|
|
tzmineastUTC/60, tzmineastUTC%60 );
|
|
if ( isAPropertyOf( locvcal, VCTimeZoneProp ) == NULL )
|
|
addPropValue( locvcal, VCTimeZoneProp, tzoffset );
|
|
if ( isAPropertyOf( locvcal, VCDCreatedProp ) == NULL )
|
|
addPropValue( locvcal, VCDCreatedProp, isodtime );
|
|
}
|
|
if ( vcal == NULL )
|
|
addList( &vcalist, vcal = locvcal );
|
|
addVObjectProp( vcal, vobj );
|
|
break;
|
|
}
|
|
return vobj;
|
|
}
|
|
|
|
/* delete vCard,vCal record
|
|
* ------------------------
|
|
*/
|
|
static void
|
|
vca_delrec( VObject * vobj )
|
|
{
|
|
switch ( vca_type( vobj ) ) {
|
|
case VCARD:
|
|
case VMEMO:
|
|
cleanVObject( delList( &vcalist, vobj ) );
|
|
break;
|
|
case VEVENT:
|
|
case VTODO:
|
|
cleanVObject( delProp( vcal, vobj ) );
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* get,set record-ID
|
|
* -----------------
|
|
*/
|
|
static ulong
|
|
vca_recid( VObject * vobj )
|
|
{
|
|
long recid;
|
|
|
|
if ( (recid = LongValue( vobj, XPilotIdProp )) == -1 )
|
|
return 0;
|
|
return (ulong)recid;
|
|
}
|
|
static void
|
|
vca_set_recid( VObject * vobj, ulong recid )
|
|
{
|
|
char * IC35id = "IC35id";
|
|
char * ouid;
|
|
char nuid[7+8+1];
|
|
|
|
ouid = StringValue( vobj, VCUniqueStringProp );
|
|
if ( !( ouid && *ouid ) /* not present yet */
|
|
|| strncmp( ouid, IC35id, strlen( IC35id ) ) == 0 ) { /* or IC35 recID */
|
|
sprintf( nuid, "IC35id-%08lX", recid );
|
|
SetString( vobj, VCUniqueStringProp, nuid ); /* korganizer wants UID */
|
|
}
|
|
SetLong( vobj, XPilotIdProp, recid );
|
|
}
|
|
/* get,set record-changeflag
|
|
* -------------------------
|
|
* gnomecard (from gnome-pim-1.2.0) does not maintain X-PILOTSTAT !
|
|
* as workaround the last-revised-time VCARD.REV is compared against
|
|
* the previous time of sync with IC35 'oldic35dt()' to obtain the
|
|
* record-status CLEAN or DIRTY.
|
|
*/
|
|
static int
|
|
vca_recstat( VObject * vobj )
|
|
{
|
|
long stat;
|
|
|
|
if ( (stat = LongValue( vobj, XPilotStatusProp )) == -1 )
|
|
return VCA_DIRTY;
|
|
if ( stat <= 0
|
|
&& vca_type( vobj ) == VCARD
|
|
&& oldic35dt() != 0
|
|
&& oldic35dt() < isodtime_to_unixtime( vobj, VCLastRevisedProp ) )
|
|
return VCA_DIRTY;
|
|
return (int)stat;
|
|
}
|
|
static void
|
|
vca_set_recstat( VObject * vobj, int stat )
|
|
{
|
|
SetLong( vobj, XPilotStatusProp, (long)stat );
|
|
}
|
|
|
|
/* vCard,vCal format operations
|
|
* ----------------------------
|
|
*/
|
|
struct pim_oper vca_oper = {
|
|
vca_open,
|
|
vca_close,
|
|
vca_rewind,
|
|
(void*(*)(int))vca_getrec,
|
|
(void*(*)(ulong))vca_getrec_byID,
|
|
(int(*)(IC35REC*,void*))vca_cmpic35rec,
|
|
(IC35REC*(*)(IC35REC*,void*))vca_updic35rec,
|
|
(void*(*)(IC35REC*))vca_putic35rec,
|
|
(void(*)(void*))vca_delrec,
|
|
(ulong(*)(void*))vca_recid,
|
|
(void(*)(void*,ulong))vca_set_recid,
|
|
(int(*)(void*))vca_recstat,
|
|
(void(*)(void*,int))vca_set_recstat,
|
|
};
|
|
|