763 lines
20 KiB
C
763 lines
20 KiB
C
/************************************************************************
|
|
* Copyright (C) 2000 Thomas Schulz *
|
|
* */
|
|
static char rcsid[] =
|
|
"$Id: vcutil.c,v 1.44 2000/12/28 02:26:31 tsch Rel $"; /*
|
|
* *
|
|
* IC35 synchronize data import/export: vCard,vCalendar utilities *
|
|
* *
|
|
*************************************************************************
|
|
* *
|
|
* ??? is "fixme" mark: sections of code needing fixes *
|
|
* *
|
|
* vca_type determine vCard,vCal record type *
|
|
* dupSubst substitute in string, dupStr()'d result *
|
|
* dupSubstNL translate NL to CRLF, dupStr()'d result *
|
|
* dupSubstCRLF translate CRLF to NL, dupStr()'d result *
|
|
* utilities for IC35 to/from vCard,vCal *
|
|
* clr_vobjdirty clear vCard,vCal record modified status
|
|
* SetModtimeIfdirty modified date+time to vCard,vCal record
|
|
* _ChangeString set property string, note if changed
|
|
* SetProp
|
|
* SetString
|
|
* StringValue
|
|
* SetLong set property long integer value
|
|
* LongValue
|
|
* _NoteId NOTE/DESCRIPTION for vCard/vCal
|
|
* _NotePropsLen
|
|
* SetNotes
|
|
* NotesValue
|
|
* SetNoteProp
|
|
* NotePropValue
|
|
* _is_stdCategory standard categories Personal,Business..
|
|
* SetCategory
|
|
* CategoryValue
|
|
* SetTel set telephone HOME/WORK/CELL/FAX
|
|
* SetEmail set Email1,Email2
|
|
* utilities for date+time conversion *
|
|
* isodtstr_to_unixtime
|
|
* isodtstr_to_ymd_or_today
|
|
* isodtime_to_unixtime
|
|
* isodtime_to_ymd
|
|
* isodtime_to_ymd_or_today
|
|
* isodtime_to_hms_or_now
|
|
* *
|
|
************************************************************************/
|
|
|
|
#include <stdio.h> /* sprintf(), .. */
|
|
#include <stdlib.h> /* atol() */
|
|
#include <string.h> /* strcpy(), .. */
|
|
#include <ctype.h> /* isprint(), .. */
|
|
#include <sys/types.h> /* size_t, .. */
|
|
#include <time.h> /* struct tm, time() .. */
|
|
|
|
#include "util.h" /* bool */
|
|
#include "dataio.h" /* newic35dt() */
|
|
#include "vcc.h" /* VObject, .. */
|
|
#include "vcutil.h"
|
|
NOTUSED(rcsid);
|
|
|
|
|
|
/* map vCard,vCal record name to numeric type
|
|
* ------------------------------------------
|
|
*/
|
|
int
|
|
vca_type( VObject * vobj )
|
|
{
|
|
const char * name;
|
|
|
|
if ( vobj == NULL )
|
|
return 0;
|
|
name = vObjectName( vobj );
|
|
if ( strcasecmp( name, VCCardProp ) == 0 ) return VCARD;
|
|
if ( strcasecmp( name, VCMemoProp ) == 0 ) return VMEMO;
|
|
if ( strcasecmp( name, VCCalProp ) == 0 ) return VCAL;
|
|
if ( strcasecmp( name, VCEventProp ) == 0 ) return VEVENT;
|
|
if ( strcasecmp( name, VCTodoProp ) == 0 ) return VTODO;
|
|
return 0;
|
|
}
|
|
|
|
/* string substitutions
|
|
* --------------------
|
|
*/
|
|
/* substitutes 'new' for sub-string 'old' in 'str' */
|
|
char * /* and returns dupStr()'sd resulting string */
|
|
dupSubst( char * str, char * old, char * new )
|
|
{ /* substitutes 'new' for sub-string 'old' in 'str' */
|
|
char *match, *nstr;
|
|
char *pdst, *psrc;
|
|
|
|
if ( str == NULL )
|
|
return NULL; /* marginal .. */
|
|
if ( ! (old && *old && new ) /* .. parameters */
|
|
|| (match = strstr( str, old )) == NULL ) /* or no match */
|
|
return dupStr( str, 0 );
|
|
nstr = dupStr( "", strlen(str) + strlen(new) - strlen(old) );
|
|
pdst = nstr; psrc = str;
|
|
while ( *psrc )
|
|
if ( psrc == match ) {
|
|
strcpy( pdst, new );
|
|
psrc += strlen( old );
|
|
pdst += strlen( new );
|
|
} else
|
|
*pdst++ = *psrc++;
|
|
return nstr;
|
|
}
|
|
char * /* substitute vCard,vCal NewLine with IC35 CRLF */
|
|
dupSubstNL( char * src )
|
|
{
|
|
char *psrc, *dst, *pdst;
|
|
size_t len;
|
|
|
|
len = strlen( src );
|
|
for ( psrc = src; *psrc; ++psrc )
|
|
if ( *psrc == '\n' )
|
|
++len;
|
|
pdst = dst = dupStr( "", len );
|
|
psrc = src, pdst = dst;
|
|
while ( *psrc ) {
|
|
if ( *psrc == '\n'
|
|
&& !( psrc > src && *(psrc-1) == '\r' ) )
|
|
*pdst++ = '\r';
|
|
*pdst++ = *psrc++;
|
|
}
|
|
*pdst = '\0';
|
|
return dst;
|
|
}
|
|
char * /* substitute IC35 CRLF with vCard,vCal NewLine */
|
|
dupSubstCRLF( char * src )
|
|
{
|
|
|
|
char *dst, *pdst;
|
|
|
|
dst = pdst = dupStr( src, 0 );
|
|
while ( *src )
|
|
if ( (*pdst = *src++) != '\r' && *src != '\n' )
|
|
++pdst;
|
|
*pdst = '\0';
|
|
return dst;
|
|
}
|
|
|
|
|
|
/* ============================================ */
|
|
/* utilities for IC35 to/from vCard,vCal */
|
|
/* ============================================ */
|
|
|
|
static bool _vobj_dirty; /* VObject changed in ic35xxx_to_vyyy() */
|
|
static char ic35fld[511+1]; /* field buffer used by SetXxx() */
|
|
|
|
/* clear vCard,vCal modified status
|
|
* --------------------------------
|
|
*/
|
|
void
|
|
clr_vobjdirty( void )
|
|
{
|
|
_vobj_dirty = FALSE;
|
|
}
|
|
|
|
/* revised date+time for vCard,vCal records
|
|
* ----------------------------------------
|
|
*/
|
|
static char *
|
|
_iso_ic35dt( const char * format )
|
|
{
|
|
time_t ic35dt;
|
|
struct tm * ptm;
|
|
static char isodtime[24];
|
|
|
|
ic35dt = newic35dt();
|
|
ptm = localtime( &ic35dt );
|
|
strftime( isodtime, sizeof(isodtime), format, ptm );
|
|
return isodtime;
|
|
}
|
|
void
|
|
SetModtimeIfdirty( VObject * vobj )
|
|
{
|
|
if ( ! _vobj_dirty )
|
|
return;
|
|
switch ( vca_type( vobj ) ) {
|
|
case VCARD: /* yyyy-mm-ddThh:mm:ss for VCARRD */
|
|
SetString( vobj, VCLastRevisedProp, _iso_ic35dt("%Y-%m-%dT%H:%M:%S") );
|
|
break;
|
|
case VEVENT:
|
|
case VTODO: /* also create-time for VEVENT,VTODO */
|
|
if ( ! isAPropertyOf( vobj, VCDCreatedProp ) )
|
|
SetString( vobj, VCDCreatedProp, _iso_ic35dt("%Y%m%dT%H%M%S") );
|
|
/* fall through */
|
|
case VMEMO: /* yyyymmddThhmmss for VEVENT,VTODO,memo */
|
|
SetString( vobj, VCLastModifiedProp, _iso_ic35dt("%Y%m%dT%H%M%S") );
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* set,get property string value
|
|
* -----------------------------
|
|
* handle VObject value types StringZ, UStringZ, Integer, Long
|
|
* determine need for QUOTED-PRINTABLE and CHARSET
|
|
* note if property / string changed in _vobj_dirty
|
|
*/
|
|
static void /* set property string, check and note if changed */
|
|
_ChangeString( VObject * vobj, char * str )
|
|
{
|
|
const char * oldstr;
|
|
|
|
switch ( vObjectValueType( vobj ) ) {
|
|
case VCVT_STRINGZ:
|
|
oldstr = dupStr( vObjectStringZValue( vobj ), 0 );
|
|
break;
|
|
case VCVT_USTRINGZ:
|
|
oldstr = fakeCString( vObjectUStringZValue( vobj ) );
|
|
break;
|
|
default:
|
|
oldstr = NULL;
|
|
}
|
|
if ( oldstr == NULL || strcmp( oldstr, str ) != 0 ) {
|
|
_vobj_dirty = TRUE;
|
|
setVObjectStringZValue( vobj, str );
|
|
}
|
|
deleteStr( oldstr );
|
|
}
|
|
VObject * /* add property if not yet there */
|
|
SetProp( VObject * vobj, const char * id )
|
|
{
|
|
VObject * vprop;
|
|
|
|
if ( (vprop = isAPropertyOf( vobj, id )) )
|
|
return vprop;
|
|
_vobj_dirty = TRUE;
|
|
return addProp( vobj, id );
|
|
}
|
|
void /* delete property */
|
|
DelProp( VObject * vobj, const char * id )
|
|
{
|
|
VObject * vprop;
|
|
|
|
if ( vobj && id && *id
|
|
&& (vprop = isAPropertyOf( vobj, id )) ) {
|
|
vprop = delProp( vobj, vprop );
|
|
cleanVObject( vprop );
|
|
_vobj_dirty = TRUE;
|
|
}
|
|
}
|
|
VObject * /* set string, check if need QUOTED-PRINTABLE or CHARSET */
|
|
SetString( VObject * vobj, const char * id, char * str )
|
|
{
|
|
VObject * vprop;
|
|
char * pstr;
|
|
bool prop_charset;
|
|
bool prop_quoted;
|
|
VObject * vpropchars;
|
|
|
|
if ( !( vobj && id && *id ) )
|
|
return NULL;
|
|
if ( !( str && *str ) ) {
|
|
DelProp( vobj, id );
|
|
return NULL;
|
|
}
|
|
if ( (vprop = isAPropertyOf( vobj, id )) == NULL )
|
|
vprop = addProp( vobj, id );
|
|
prop_quoted = prop_charset = FALSE;
|
|
for ( pstr = str; *pstr; ++pstr ) {
|
|
if ( *pstr & 0x80 )
|
|
prop_charset = TRUE;
|
|
if ( ! isprint( *pstr ) )
|
|
prop_quoted = TRUE;
|
|
}
|
|
if ( prop_quoted || prop_charset ) {
|
|
/*
|
|
* single-field property like VCARD:FN needs charset,quoted property
|
|
* attached to the property 'vprop'
|
|
* multi-field sub-property like VCARD:ADR:VCCityProp needs charset
|
|
* prop attached to the toplevel property 'vobj'
|
|
* multi-field property must not use QUOTED-PRINTABLE due to problem
|
|
* with vcc.y parser. fixing the parser would not help, because other
|
|
* programs using the vCard,vCal output do not have the fixed parser.
|
|
*/
|
|
switch ( vca_type( vobj ) ) {
|
|
case VCARD:
|
|
case VMEMO:
|
|
case VEVENT:
|
|
case VTODO:
|
|
vpropchars = vprop; /* single-field property */
|
|
break;
|
|
default:
|
|
vpropchars = vobj; /* multi-field sub-property */
|
|
}
|
|
if ( prop_quoted /* QUOTED-PRINTABLE must not */
|
|
&& vpropchars != vobj ) /* be used with multi-field */
|
|
SetProp( vpropchars, VCQuotedPrintableProp );
|
|
if ( prop_charset )
|
|
SetString( vpropchars, VCCharacterSetProp, "ISO-8859-1" );
|
|
}
|
|
_ChangeString( vprop, str );
|
|
return vprop;
|
|
}
|
|
char * /* dupStr()'d property's string value or NULL */
|
|
dupStringValue( VObject * vobj, const char * id )
|
|
{
|
|
VObject * vprop;
|
|
char vtext[1+10+1];
|
|
|
|
if ( vobj == NULL )
|
|
return NULL;
|
|
if ( id && *id ) {
|
|
if ( (vprop = isAPropertyOf( vobj, id )) == NULL )
|
|
return NULL;
|
|
} else
|
|
vprop = vobj;
|
|
switch ( vObjectValueType( vprop ) ) {
|
|
case VCVT_STRINGZ:
|
|
return dupStr( vObjectStringZValue( vprop ), 0 );
|
|
case VCVT_USTRINGZ:
|
|
return fakeCString( vObjectUStringZValue( vprop ) );
|
|
case VCVT_UINT:
|
|
sprintf( vtext, "%+010d", vObjectIntegerValue( vprop ) );
|
|
return dupStr( vtext, 0 );
|
|
case VCVT_ULONG:
|
|
sprintf( vtext, "%+010ld", vObjectLongValue( vprop ) );
|
|
return dupStr( vtext, 0 );
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
char * /* property's string value or "" */
|
|
StringValue( VObject * vobj, const char * id )
|
|
{
|
|
char * strval;
|
|
|
|
if ( (strval = dupStringValue( vobj, id )) == NULL )
|
|
return "";
|
|
strncat( strcpy( ic35fld, "" ), strval, sizeof(ic35fld)-1 );
|
|
deleteStr( strval );
|
|
return ic35fld;
|
|
}
|
|
|
|
/* set,get property long integer value
|
|
* -----------------------------------
|
|
*/
|
|
void
|
|
SetLong( VObject * vobj, const char * id, long value )
|
|
{
|
|
VObject * vprop;
|
|
|
|
if ( (vprop = isAPropertyOf( vobj, id )) == NULL )
|
|
vprop = addProp( vobj, id );
|
|
setVObjectLongValue( vprop, value );
|
|
}
|
|
long /* property's long value or -1 if unknown */
|
|
LongValue( VObject * vobj, const char * id )
|
|
{
|
|
VObject * vprop;
|
|
char * strval;
|
|
long value;
|
|
|
|
if ( !( vobj && id && *id )
|
|
|| (vprop = isAPropertyOf( vobj, id )) == NULL )
|
|
return -1;
|
|
switch ( vObjectValueType( vprop ) ) {
|
|
case VCVT_STRINGZ:
|
|
return atol( vObjectStringZValue( vprop ) );
|
|
case VCVT_USTRINGZ:
|
|
strval = fakeCString( vObjectUStringZValue( vprop ) );
|
|
value = atol( strval );
|
|
deleteStr( strval );
|
|
return value;
|
|
case VCVT_UINT:
|
|
return vObjectIntegerValue( vprop );
|
|
case VCVT_ULONG:
|
|
return vObjectLongValue( vprop );
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* set,get VObject NOTE/DESCRIPTION
|
|
* --------------------------------
|
|
* some properties are not supported by e.g. korganizer, gnomecal,
|
|
* as workaround they are kept is marked lines in NOTE/DESCRIPTION.
|
|
*/
|
|
static const char *
|
|
_NoteId( VObject * vobj )
|
|
{
|
|
switch( vca_type( vobj ) ) {
|
|
case VCARD:
|
|
case VMEMO: return VCNoteProp;
|
|
case VEVENT:
|
|
case VTODO: return VCDescriptionProp;
|
|
}
|
|
return NULL;
|
|
}
|
|
static size_t
|
|
_NotePropsLen( char * str, int vtype )
|
|
{
|
|
static struct {
|
|
int type;
|
|
const char * id;
|
|
} proptab[] = {
|
|
{ VCARD, DEF1PROP },
|
|
{ VCARD, DEF2PROP },
|
|
{ VTODO, VCDTstartProp },
|
|
{ VTODO, VCDueProp },
|
|
{ VTODO, VCCategoriesProp },
|
|
{ 0, NULL }
|
|
},
|
|
*ptab;
|
|
char * pline;
|
|
size_t lid;
|
|
|
|
for ( pline = str; pline; pline = strchr( pline, '\n' ) ) {
|
|
if ( *pline == '\n' )
|
|
++pline;
|
|
for ( ptab = proptab; ptab->id != NULL; ++ptab )
|
|
if ( ptab->type == vtype ) {
|
|
lid = strlen( ptab->id );
|
|
if ( strncmp( pline, ptab->id, lid ) == 0
|
|
&& *(pline+lid) == ':' )
|
|
break;
|
|
}
|
|
if ( ptab->id == NULL )
|
|
break;
|
|
}
|
|
return pline ? pline - str : 0;
|
|
}
|
|
void
|
|
SetNotes( VObject * vobj, char * value )
|
|
{
|
|
/* preserve property values stored in NOTE,DESCRIPTION */
|
|
/* translate CRLF to NL to avoid confusing korganizer */
|
|
char * onote;
|
|
char * nnote;
|
|
size_t lprops;
|
|
const char *id;
|
|
|
|
if ( (id = _NoteId( vobj )) == NULL )
|
|
return;
|
|
value = dupSubstCRLF( value ? value : "" );
|
|
if ( (onote = StringValue( vobj, id )) != NULL && *onote ) {
|
|
lprops = _NotePropsLen( onote, vca_type( vobj ) );
|
|
nnote = dupStr( "", lprops + strlen(value) );
|
|
strcat( strncat( strcpy( nnote, "" ), onote, lprops ), value );
|
|
deleteStr( value );
|
|
} else {
|
|
nnote = value;
|
|
}
|
|
SetString( vobj, id, nnote );
|
|
deleteStr( nnote );
|
|
}
|
|
char *
|
|
NotesValue( VObject * vobj )
|
|
{
|
|
/* skip property values stored in NOTE,DESCRIPTION */
|
|
/* translate NL to CRLF to avoid confusing IC35 */
|
|
char * note;
|
|
size_t lprops;
|
|
|
|
if ( (note = StringValue( vobj, _NoteId( vobj ) )) == NULL
|
|
|| strlen( note ) == 0 )
|
|
return "";
|
|
lprops = _NotePropsLen( note, vca_type( vobj ) );
|
|
note = dupSubstNL( note+lprops );
|
|
strcat( strcpy( ic35fld, "" ), note );
|
|
deleteStr( note );
|
|
return ic35fld;
|
|
}
|
|
void
|
|
SetNoteProp( VObject * vobj, const char * id, char * value )
|
|
{
|
|
/* replace or prepend property value in NOTE/DESCRIPTION */
|
|
/* as marked line "<id>:<value>\n" */
|
|
char *onote, *oprop, *oldstr;
|
|
char *nnote, *nprop;
|
|
const char *noteid;
|
|
|
|
if ( !((noteid = _NoteId( vobj )) && id && *id ) )
|
|
return;
|
|
onote = dupSubstNL( StringValue( vobj, noteid ) );
|
|
if ( value && *value ) {
|
|
nprop = dupStr( "", strlen(id)+1+strlen(value)+2 );
|
|
sprintf( nprop, "%s:%s\r\n", id, value );
|
|
} else
|
|
nprop = dupStr( "", 0 );
|
|
if ( (oldstr = NotePropValue( vobj, id )) != NULL && *oldstr ) {
|
|
oprop = dupStr( "", strlen(id)+1+strlen(oldstr)+2 );
|
|
sprintf( oprop, "%s:%s\r\n", id, oldstr );
|
|
nnote = dupSubst( onote, oprop, nprop );
|
|
deleteStr( oprop );
|
|
} else {
|
|
nnote = dupStr( "", strlen(nprop)+strlen(onote) );
|
|
strcat( strcpy( nnote, nprop ), onote );
|
|
}
|
|
deleteStr( onote );
|
|
deleteStr( nprop );
|
|
nnote = dupSubstCRLF( oldstr = nnote ); deleteStr( oldstr );
|
|
SetString( vobj, noteid, nnote );
|
|
deleteStr( nnote );
|
|
}
|
|
char *
|
|
NotePropValue( VObject * vobj, const char * id )
|
|
{
|
|
/* retrieve property value stored in NOTE/DESCRIPTION */
|
|
/* from marked line "<id>:<value>\n" */
|
|
char * note;
|
|
char * linetag;
|
|
char * pprop;
|
|
char * ptr;
|
|
|
|
if ( (note = StringValue( vobj, _NoteId( vobj ) )) == NULL
|
|
|| strlen( note ) == 0 ) {
|
|
return "";
|
|
}
|
|
sprintf( ptr = dupStr( "", strlen(note)+1 ), "\n%s", note );
|
|
note = dupSubstNL( ptr );
|
|
deleteStr( ptr );
|
|
sprintf( linetag = dupStr( "", strlen(id)+3 ), "\r\n%s:", id );
|
|
if ( (pprop = strstr( note, linetag )) != NULL ) {
|
|
pprop += strlen( linetag );
|
|
if ( (ptr = strstr( pprop, "\r\n" )) != NULL )
|
|
*ptr = '\0';
|
|
strncat( strcpy( ic35fld, "" ), pprop, sizeof(ic35fld) );
|
|
}
|
|
deleteStr( linetag );
|
|
deleteStr( note );
|
|
return pprop ? ic35fld : "";
|
|
}
|
|
|
|
/* categories
|
|
* ----------
|
|
*/
|
|
static char *
|
|
_is_stdCategory( char * category )
|
|
{
|
|
char * Address = "Address";
|
|
char * Business = "Business";
|
|
char * Personal = "Personal";
|
|
char * Unfiled = "Unfiled";
|
|
|
|
if ( strcasecmp( category, Address ) == 0 )
|
|
return Address;
|
|
else if ( strcasecmp( category, Business ) == 0 )
|
|
return Business;
|
|
else if ( strcasecmp( category, Personal ) == 0 )
|
|
return Personal;
|
|
else if ( strcasecmp( category, Unfiled ) == 0 )
|
|
return Unfiled;
|
|
return NULL;
|
|
}
|
|
VObject *
|
|
SetCategory( VObject * vobj, char * newcategory )
|
|
{
|
|
char * categories;
|
|
char * oldcategories;
|
|
char * category;
|
|
VObject * vprop;
|
|
|
|
if ( (oldcategories = StringValue( vobj, VCCategoriesProp )) == NULL
|
|
|| strlen( oldcategories ) == 0 /* no categories or */
|
|
|| strchr( oldcategories, ';' ) == NULL ) /* single category */
|
|
return SetString( vobj, VCCategoriesProp, newcategory );
|
|
categories = dupStr( oldcategories, strlen( oldcategories )
|
|
+ 1 + strlen( newcategory ) );
|
|
for ( category = strtok( categories, ";" );
|
|
category != NULL; category = strtok( NULL, ";" ) ) {
|
|
if ( strcmp( category, newcategory ) == 0 )
|
|
return isAPropertyOf( vobj, VCCategoriesProp );
|
|
if ( _is_stdCategory( category ) )
|
|
break;
|
|
}
|
|
if ( category != NULL ) { /* replace standard category */
|
|
category = dupStr( category, 0 );
|
|
deleteStr( categories );
|
|
categories = dupSubst( oldcategories, category, newcategory );
|
|
deleteStr( category );
|
|
} else /* else prepend new category */
|
|
sprintf( categories, "%s;%s", newcategory, oldcategories );
|
|
vprop = SetString( vobj, VCCategoriesProp, categories );
|
|
deleteStr( categories );
|
|
return vprop;
|
|
}
|
|
char * /* single, first standard or "Unfiled" */
|
|
CategoryValue( VObject * vobj )
|
|
{
|
|
char * Unfiled = "Unfiled";
|
|
char * categories;
|
|
char * category;
|
|
char * firstcategory;
|
|
char * firststdcategory;
|
|
|
|
if ( (categories = StringValue( vobj, VCCategoriesProp )) == NULL
|
|
|| strlen( categories ) == 0 )
|
|
return Unfiled;
|
|
firstcategory = firststdcategory = NULL;
|
|
for ( category = strtok( categories, ";" );
|
|
category; category = strtok( NULL, ";" ) ) {
|
|
if ( firstcategory == NULL )
|
|
firstcategory = dupStr( category, 0 );
|
|
if ( firststdcategory == NULL )
|
|
firststdcategory = _is_stdCategory( category );
|
|
}
|
|
if ( (category = firststdcategory) == NULL ) {
|
|
if ( firstcategory )
|
|
category = strncat( strcpy( ic35fld, "" ),
|
|
firstcategory, sizeof(ic35fld)-1 );
|
|
else
|
|
category = Unfiled;
|
|
}
|
|
deleteStr( firstcategory );
|
|
return category;
|
|
}
|
|
|
|
/* telephone numbers
|
|
* -----------------
|
|
*/
|
|
VObject *
|
|
SetTel( VObject * vobj, const char * type, char * str )
|
|
{
|
|
VObject * vprop;
|
|
VObjectIterator iter;
|
|
|
|
initPropIterator( &iter, vobj );
|
|
while ( moreIteration( &iter ) ) {
|
|
vprop = nextVObject( &iter );
|
|
if ( strcmp( vObjectName( vprop ), VCTelephoneProp ) == 0
|
|
&& isAPropertyOf( vprop, type ) ) {
|
|
if ( str && *str ) {
|
|
_ChangeString( vprop, str );
|
|
return vprop;
|
|
} else {
|
|
cleanVObject( delProp( vobj, vprop ) );
|
|
_vobj_dirty = TRUE;
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
if ( !( str && *str ) )
|
|
return NULL;
|
|
_vobj_dirty = TRUE;
|
|
vprop = addPropValue( vobj, VCTelephoneProp, str );
|
|
addProp( vprop, type );
|
|
return vprop;
|
|
}
|
|
|
|
/* email addresses
|
|
* ---------------
|
|
*/
|
|
VObject *
|
|
SetEmail( VObject * vobj, int num, char * str )
|
|
{
|
|
VObject * vprop;
|
|
VObjectIterator iter;
|
|
int index;
|
|
|
|
index = 0;
|
|
initPropIterator( &iter, vobj );
|
|
while ( moreIteration( &iter ) ) {
|
|
vprop = nextVObject( &iter );
|
|
if ( strcmp( vObjectName( vprop ), VCEmailAddressProp ) == 0
|
|
&& ++index == num ) {
|
|
if ( str && *str ) {
|
|
_ChangeString( vprop, str );
|
|
return vprop;
|
|
} else {
|
|
cleanVObject( delProp( vobj, vprop ) );
|
|
_vobj_dirty = TRUE;
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
if ( !( str && *str ) )
|
|
return NULL;
|
|
_vobj_dirty = TRUE;
|
|
vprop = addPropValue( vobj, VCEmailAddressProp, str );
|
|
addProp( vprop, VCInternetProp );
|
|
return vprop;
|
|
}
|
|
|
|
|
|
/* ============================================ */
|
|
/* ISO date+time conversions */
|
|
/* ============================================ */
|
|
|
|
static time_t /* convert ISO date+time to Unix-time */
|
|
isodtstr_to_unixtime( char * dtstr )
|
|
{
|
|
char * format;
|
|
struct tm dtm;
|
|
time_t dtime;
|
|
|
|
if ( !( dtstr && *dtstr ) )
|
|
return -1;
|
|
|
|
if ( strspn( dtstr, "0123456789" ) == 8 )
|
|
format = "%4d" "%2d" "%2d%*[^0-9]%2d" "%2d" "%2d";
|
|
else
|
|
format = "%4d%*[^0-9]%2d%*[^0-9]%2d%*[^0-9]%2d%*[^0-9]%2d%*[^0-9]%2d";
|
|
dtime = time( NULL );
|
|
memcpy( &dtm, localtime( &dtime ), sizeof(dtm) ); /* default: today,now */
|
|
sscanf( dtstr, format, &dtm.tm_year, &dtm.tm_mon, &dtm.tm_mday,
|
|
&dtm.tm_hour, &dtm.tm_min, &dtm.tm_sec );
|
|
dtm.tm_year -= 1900;
|
|
dtm.tm_mon -= 1;
|
|
dtm.tm_isdst = -1;
|
|
dtime = mktime( &dtm );
|
|
/*??? ymdhms_from_isodtime() does not respect timezone/Zulu in isodtime ???*/
|
|
|
|
return dtime;
|
|
}
|
|
char *
|
|
isodtstr_to_ymd_or_today( char * dtstr )
|
|
{
|
|
static char ymd[8+1];
|
|
time_t utsec;
|
|
|
|
if ( (utsec = isodtstr_to_unixtime( dtstr )) == -1 )
|
|
utsec = time( NULL );
|
|
strftime( ymd, sizeof(ymd), "%Y%m%d", localtime( &utsec ) );
|
|
return ymd;
|
|
}
|
|
time_t /* convert ISO date+time to Unix-time */
|
|
isodtime_to_unixtime( VObject * vobj, const char * id )
|
|
{
|
|
VObject * vprop;
|
|
char * dtstr;
|
|
time_t dtime;
|
|
|
|
if ( (vprop = isAPropertyOf( vobj, id )) == NULL )
|
|
return -1;
|
|
dtstr = fakeCString( vObjectUStringZValue( vprop ) );
|
|
dtime = isodtstr_to_unixtime( dtstr );
|
|
deleteStr( dtstr );
|
|
return dtime;
|
|
}
|
|
char *
|
|
isodtime_to_ymd( VObject * vobj, const char * id )
|
|
{
|
|
static char ymd[8+1];
|
|
time_t utsec;
|
|
|
|
if ( (utsec = isodtime_to_unixtime( vobj, id )) == -1 )
|
|
return "";
|
|
strftime( ymd, sizeof(ymd), "%Y%m%d", localtime( &utsec ) );
|
|
return ymd;
|
|
}
|
|
char *
|
|
isodtime_to_ymd_or_today( VObject * vobj, const char * id )
|
|
{
|
|
static char ymd[8+1];
|
|
time_t utsec;
|
|
|
|
if ( (utsec = isodtime_to_unixtime( vobj, id )) == -1 )
|
|
utsec = time( NULL );
|
|
strftime( ymd, sizeof(ymd), "%Y%m%d", localtime( &utsec ) );
|
|
return ymd;
|
|
}
|
|
char *
|
|
isodtime_to_hms_or_now( VObject * vobj, const char * id )
|
|
{
|
|
static char hms[6+1];
|
|
time_t utsec;
|
|
|
|
if ( (utsec = isodtime_to_unixtime( vobj, id )) == -1 )
|
|
utsec = time( NULL );
|
|
strftime( hms, sizeof(hms), "%H%M%S", localtime( &utsec ) );
|
|
return hms;
|
|
}
|