1 line
58 KiB
C
Executable File
1 line
58 KiB
C
Executable File
/* Copyright (c) 2017, Computer History Museum
|
||
All rights reserved.
|
||
Redistribution and use in source and binary forms, with or without modification, are permitted (subject to
|
||
the limitations in the disclaimer below) provided that the following conditions are met:
|
||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
|
||
disclaimer in the documentation and/or other materials provided with the distribution.
|
||
* Neither the name of Computer History Museum nor the names of its contributors may be used to endorse or promote products
|
||
derived from this software without specific prior written permission.
|
||
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE
|
||
COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
||
DAMAGE. */
|
||
|
||
#define FILE_NUM 141
|
||
|
||
#ifdef VCARD
|
||
|
||
/* Copyright (c) 2000 by QUALCOMM Incorporated */
|
||
|
||
#define isCR(aChar) ((aChar) == 13)
|
||
#define isLF(aChar) ((aChar) == 10)
|
||
#define isHTAB(aChar) ((aChar) == 9)
|
||
#define isSPACE(aChar) ((aChar) == 32)
|
||
#define isCOLON(aChar) ((aChar) == ':')
|
||
#define isSEMI(aChar) ((aChar) == ';')
|
||
#define isDASH(aChar) ((aChar) == '-')
|
||
#define isCRLF(aChar) (isCR (aChar) || isLF (aChar))
|
||
#define isEQUAL(aChar) ((aChar) == '=')
|
||
#define isEscape(aChar) ((aChar) == '\\')
|
||
|
||
typedef struct {
|
||
Handle data; // Data containing the entire vCard
|
||
Ptr spot; // Spot within the vCard where we're currently reading
|
||
Ptr end; // Points to the final byte in the vCard
|
||
VCardErrorType error; // Errors detected in the vCard source by the parser
|
||
XDashStringHandle xdash; // Handle to all the vCard extensions the client knows about
|
||
VCardItemRec item; // Information about the item currently being parsed
|
||
VCardItemProc itemProc; // Callback for each parsed item in the vCard
|
||
VCardErrorProc errorProc; // Callback for each parsing
|
||
long refcon; // Private data owned by the caller, passed back during itemProcs
|
||
} VCardParserRec, *VCardParserPtr, **VCardParserHandle;
|
||
|
||
|
||
//
|
||
// Static Prototypes
|
||
//
|
||
|
||
static OSErr VCardAccuAddValue (AccuPtr a, Handle value, Boolean quotedPrintable);
|
||
static OSErr VCardAccuAddValueQuotedPrintable (AccuPtr a, Handle value);
|
||
static OSErr VCardAccuMaybeQuotedPrintableSoftline (AccuPtr a, Handle value, long *offset, long *count, long maxChar);
|
||
static Boolean VCardValueContainsCRLFs (Handle value);
|
||
static OSErr VCardAccuAddProperties (AccuPtr a, short count, short *vcProp);
|
||
static OSErr VCardAccuAddPropertiesAndValue (AccuPtr a, short count, short *vcProp, Handle value, Boolean quotedPrintable);
|
||
static OSErr VCardAccuAddValueList (AccuPtr a, Handle notes, short count, short *abTag, Boolean *anyValues);
|
||
static OSErr VCardAccuAddAVPairStr (AccuPtr a, short attributeResIndex, PStr value);
|
||
static OSErr VCardAccuAddAVPairRes (AccuPtr a, short attributeResIndex, short valueResIndex);
|
||
static OSErr VCardAccuAddAddress (AccuPtr a, Handle notes, Boolean home, Boolean preferred);
|
||
static OSErr VCardAccuAddPhone (AccuPtr a, Handle notes, short propertyID, short propertyLocation, short tagID, Boolean preferred);
|
||
static OSErr VCardAccuAddURL (AccuPtr a, Handle notes, short propertyLocation, short tagID, Boolean preferred);
|
||
static OSErr VCardAccuAddOtherEmail (AccuPtr a, Handle notes);
|
||
static OSErr VCardAccuAddOther (AccuPtr a, Handle notes, short propertyID, short tagID);
|
||
static OSErr VCardAccuAddNotes (AccuPtr a, Handle notes);
|
||
static OSErr VCardParserVCardFile (VCardParserPtr parserPtr);
|
||
static OSErr VCardParserVCard (VCardParserPtr parserPtr);
|
||
static OSErr VCardParserItems (VCardParserPtr parserPtr);
|
||
static OSErr VCardParserItem (VCardParserPtr parserPtr);
|
||
static OSErr VCardParserGroups (VCardParserPtr parserPtr, VCardItemPtr itemPtr, Str255 keyword);
|
||
static OSErr VCardParserParameters (VCardParserPtr parserPtr, VCardItemPtr itemPtr);
|
||
static void VCardParserParameterTypeValue (VCardParserPtr parserPtr, VCardParamPtr paramPtr);
|
||
static void VCardParserParameterValueValue (VCardParserPtr parserPtr, VCardParamPtr paramPtr);
|
||
static void VCardParserParameterEncodingValue (VCardParserPtr parserPtr, VCardParamPtr paramPtr);
|
||
static void VCardParserParameterCharsetValue (VCardParserPtr parserPtr, VCardParamPtr paramPtr);
|
||
static void VCardParserParameterLangaugeValue (VCardParserPtr parserPtr, VCardParamPtr paramPtr);
|
||
static void VCardParserProcessOrphanedEncoding (VCardItemPtr itemPtr, vcKeywordType encodingValue);
|
||
static void VCardParserEqualSign (VCardParserPtr parserPtr);
|
||
static OSErr VCardParserValue (VCardParserPtr parserPtr, VCardItemPtr itemPtr);
|
||
static OSErr VCardParserAddress (VCardParserPtr parserPtr, VCardItemPtr itemPtr);
|
||
static OSErr VCardParserOrganization (VCardParserPtr parserPtr, VCardItemPtr itemPtr);
|
||
static OSErr VCardParserName (VCardParserPtr parserPtr, VCardItemPtr itemPtr);
|
||
static OSErr VCardParserPropertPartList (VCardParserPtr parserPtr, VCardItemPtr itemPtr, vcKeywordType *partList, short parts);
|
||
static OSErr VCardParser7BitValue (VCardParserPtr parserPtr, VCardItemPtr itemPtr, Boolean valueList, Boolean quotedPrintable);
|
||
static OSErr VCardParser8BitValue (VCardParserPtr parserPtr, VCardItemPtr itemPtr);
|
||
static OSErr VCardParserQuotedPrintableValue (VCardParserPtr parserPtr, VCardItemPtr itemPtr, char delimiter);
|
||
static OSErr VCardParserBase64Value (VCardParserPtr parserPtr, VCardItemPtr itemPtr);
|
||
static void VCardParserSkipToNextLine (VCardParserPtr parserPtr);
|
||
static Boolean VCardParserWS (VCardParserPtr parserPtr, Boolean optional);
|
||
static Boolean VCardParserWSLS (VCardParserPtr parserPtr, Boolean optional);
|
||
static OSErr VCardParserStrnosemi (VCardParserPtr parserPtr, VCardItemPtr itemPtr);
|
||
static PStr VCardParserKeyword (VCardParserPtr parserPtr, PStr keyword);
|
||
static OSErr VCardParserWord (VCardParserPtr parserPtr, Handle value);
|
||
static Boolean VCardParserCharacter (VCardParserPtr parserPtr, char c, Boolean optional);
|
||
static Boolean VCardParseCRLFs (VCardParserPtr parserPtr, Boolean optional);
|
||
static OSErr VCardAppendKeywordToGroup (VCardItemPtr itemPtr, Str255 keyword);
|
||
static OSErr VCardParserAppendPropertyValueString (VCardParserPtr parserPtr, VCardParamPtr paramPtr, Handle strings);
|
||
static vcKeywordType VCardProcessProperty (VCardParserPtr parserPtr, Str255 keyword);
|
||
static short FindXDashString (XDashStringHandle xdash, PStr keyword);
|
||
|
||
|
||
Boolean IsVCardAvailable (void)
|
||
|
||
{
|
||
return (HasFeature (featureVCard) && PrefIsSet (PREF_VCARD));
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// MakeVCard
|
||
//
|
||
// Accepts standard nickname address and notes handle and returns a handle
|
||
// containing the text of a vCard. For now, our email expansion will be
|
||
// mapped in full to vcard's single address field. Dunno if this will
|
||
// actually be acceptable to other vcard savvy apps... we'll see...
|
||
//
|
||
// For now, we're just going to built vCards by brute force, but it would be nice
|
||
// if we could build a more portable means of building vCards based on templates.
|
||
//
|
||
// We are also ingnoring several Eudora nickname fields when exporting as a vCard:
|
||
// <09> Other Email
|
||
// <09> Other Phone
|
||
// <09> Other Web Pages
|
||
// <09> Notes (though we'll probably add this later)
|
||
//
|
||
// We also need to encoded fields with values containing
|
||
//
|
||
|
||
#define kVCardLineMaxValues 10
|
||
|
||
Handle MakeVCard (Handle addresses, Handle notes)
|
||
|
||
{
|
||
Accumulator vCard;
|
||
PrimaryLocationType preferred;
|
||
Str255 scratch;
|
||
OSErr theError;
|
||
long oldOffset;
|
||
short abTag[kVCardLineMaxValues], // More than enough room for now
|
||
vcProp[kVCardLineMaxValues], // More than enough room for now
|
||
count;
|
||
Boolean anyValues,
|
||
quotedPrintable;
|
||
|
||
if (!IsVCardAvailable ())
|
||
return (nil);
|
||
|
||
UseFeature (featureVCard);
|
||
|
||
preferred = GetPrimaryLocation (notes);
|
||
|
||
// Initialize an accumulator for the vCard data
|
||
theError = AccuInit (&vCard);
|
||
|
||
// BEGIN:VCARD
|
||
theError = VCardAccuAddAVPairRes (&vCard, vcBegin, vcVCard);
|
||
|
||
// VERSION:2.1
|
||
if (!theError)
|
||
theError = VCardAccuAddAVPairStr (&vCard, vcVersion, "\p2.1");
|
||
|
||
// Add the body of the vCard
|
||
|
||
// Full name
|
||
oldOffset = vCard.offset;
|
||
if (!theError)
|
||
theError = AccuAddStr (&vCard, GetRString (scratch, VCardKeywordStrn + vcFN));
|
||
if (!theError) {
|
||
abTag[0] = abTagName;
|
||
theError = VCardAccuAddValueList (&vCard, notes, 1, abTag, &anyValues);
|
||
}
|
||
if (!anyValues)
|
||
vCard.offset = oldOffset;
|
||
|
||
// Name
|
||
oldOffset = vCard.offset;
|
||
if (!theError)
|
||
theError = AccuAddStr (&vCard, GetRString (scratch, VCardKeywordStrn + vcN));
|
||
if (!theError) {
|
||
abTag[0] = abTagLast;
|
||
abTag[1] = abTagFirst;
|
||
abTag[2] = abTag[3] = abTag[4] = 0;
|
||
theError = VCardAccuAddValueList (&vCard, notes, 5, abTag, &anyValues);
|
||
}
|
||
if (!anyValues)
|
||
vCard.offset = oldOffset;
|
||
|
||
// Email
|
||
if (!theError && addresses) {
|
||
quotedPrintable = VCardValueContainsCRLFs (addresses);
|
||
vcProp[0] = vcEmail;
|
||
vcProp[1] = vcInternet;
|
||
vcProp[2] = vcPref;
|
||
count = 3;
|
||
if (quotedPrintable = VCardValueContainsCRLFs (addresses))
|
||
vcProp[count++] = vcQuotedPrintable;
|
||
|
||
theError = VCardAccuAddPropertiesAndValue (&vCard, count, vcProp, addresses, quotedPrintable);
|
||
}
|
||
|
||
// Home Address
|
||
if (!theError)
|
||
theError = VCardAccuAddAddress (&vCard, notes, true, preferred == homePrimary);
|
||
|
||
// Work Address
|
||
if (!theError)
|
||
theError = VCardAccuAddAddress (&vCard, notes, false, preferred == workPrimary);
|
||
|
||
// Home voice phone
|
||
if (!theError)
|
||
theError = VCardAccuAddPhone (&vCard, notes, vcVoice, vcHome, abTagPhone, preferred == homePrimary);
|
||
|
||
// Home fax
|
||
if (!theError)
|
||
theError = VCardAccuAddPhone (&vCard, notes, vcFax, vcHome, abTagFax, preferred == homePrimary);
|
||
|
||
// Home cell phone
|
||
if (!theError)
|
||
theError = VCardAccuAddPhone (&vCard, notes, vcCell, vcHome, abTagMobile, preferred == homePrimary);
|
||
|
||
// Work voice phone
|
||
if (!theError)
|
||
theError = VCardAccuAddPhone (&vCard, notes, vcVoice, vcWork, abTagPhone2, preferred == workPrimary);
|
||
|
||
// Work fax
|
||
if (!theError)
|
||
theError = VCardAccuAddPhone (&vCard, notes, vcFax, vcWork, abTagFax2, preferred == workPrimary);
|
||
|
||
// Work cell phone
|
||
if (!theError)
|
||
theError = VCardAccuAddPhone (&vCard, notes, vcCell, vcWork, abTagMobile2, preferred == workPrimary);
|
||
|
||
// Title
|
||
oldOffset = vCard.offset;
|
||
if (!theError)
|
||
theError = AccuAddStr (&vCard, GetRString (scratch, VCardKeywordStrn + vcTitle));
|
||
if (!theError) {
|
||
abTag[0] = abTagTitle;
|
||
theError = VCardAccuAddValueList (&vCard, notes, 1, abTag, &anyValues);
|
||
}
|
||
if (!anyValues)
|
||
vCard.offset = oldOffset;
|
||
|
||
// Company
|
||
oldOffset = vCard.offset;
|
||
if (!theError)
|
||
theError = AccuAddStr (&vCard, GetRString (scratch, VCardKeywordStrn + vcOrg));
|
||
if (!theError) {
|
||
abTag[0] = abTagCompany;
|
||
abTag[1] = 0;
|
||
theError = VCardAccuAddValueList (&vCard, notes, 2, abTag, &anyValues);
|
||
}
|
||
if (!anyValues)
|
||
vCard.offset = oldOffset;
|
||
|
||
// Home URL
|
||
if (!theError)
|
||
theError = VCardAccuAddURL (&vCard, notes, vcHome, abTagWeb, preferred == homePrimary);
|
||
|
||
// Work URL
|
||
if (!theError)
|
||
theError = VCardAccuAddURL (&vCard, notes, vcWork, abTagWeb2, preferred == workPrimary);
|
||
|
||
// Other Email
|
||
if (!theError)
|
||
theError = VCardAccuAddOtherEmail (&vCard, notes);
|
||
|
||
// Other Phone
|
||
if (!theError)
|
||
theError = VCardAccuAddOther (&vCard, notes, vcTel, abTagOtherPhone);
|
||
|
||
// Other URL
|
||
if (!theError)
|
||
theError = VCardAccuAddOther (&vCard, notes, vcURL, abTagOtherWeb);
|
||
|
||
// Notes
|
||
if (!theError)
|
||
theError = VCardAccuAddNotes (&vCard, notes);
|
||
|
||
// END:VCARD
|
||
if (!theError)
|
||
theError = VCardAccuAddAVPairRes (&vCard, vcEnd, vcVCard);
|
||
|
||
if (!theError)
|
||
AccuTrim (&vCard);
|
||
else
|
||
AccuZap (vCard);
|
||
return (vCard.data);
|
||
}
|
||
|
||
|
||
static OSErr VCardAccuAddValue (AccuPtr a, Handle value, Boolean quotedPrintable)
|
||
|
||
{
|
||
Ptr spot,
|
||
end;
|
||
OSErr theError;
|
||
long offset;
|
||
|
||
theError = noErr;
|
||
if (value) {
|
||
if (quotedPrintable)
|
||
return (VCardAccuAddValueQuotedPrintable (a, value));
|
||
|
||
offset = 0;
|
||
spot = LDRef (value);
|
||
end = spot + GetHandleSize (value);
|
||
|
||
while (spot < end && !theError) {
|
||
if (isSEMI (*spot)) {
|
||
theError = AccuAddFromHandle (a, value, offset, spot - *value - offset);
|
||
if (!theError)
|
||
theError = AccuAddChar (a, '\\');
|
||
offset = spot - *value;
|
||
}
|
||
++spot;
|
||
}
|
||
if (!theError)
|
||
theError = AccuAddFromHandle (a, value, offset, end - *value - offset);
|
||
UL (value);
|
||
}
|
||
return (theError);
|
||
}
|
||
|
||
|
||
static OSErr VCardAccuAddValueQuotedPrintable (AccuPtr a, Handle value)
|
||
|
||
{
|
||
Ptr spot,
|
||
end;
|
||
OSErr theError;
|
||
long offset,
|
||
count;
|
||
|
||
theError = noErr;
|
||
offset = 0;
|
||
count = 0;
|
||
spot = LDRef (value);
|
||
end = spot + GetHandleSize (value);
|
||
|
||
while (spot < end && !theError) {
|
||
theError = VCardAccuMaybeQuotedPrintableSoftline (a, value, &offset, &count, 74); // Room for "="
|
||
if (!theError)
|
||
switch (*spot) {
|
||
case ';':
|
||
theError = VCardAccuMaybeQuotedPrintableSoftline (a, value, &offset, &count, 72); // Room for "\;="
|
||
if (!theError)
|
||
theError = AccuAddFromHandle (a, value, offset, spot - *value - offset);
|
||
if (!theError)
|
||
theError = AccuAddChar (a, '\\');
|
||
offset = spot - *value;
|
||
++count;
|
||
break;
|
||
case '\r':
|
||
case '\n':
|
||
theError = VCardAccuMaybeQuotedPrintableSoftline (a, value, &offset, &count, 68); // Room for "=0D=0A="
|
||
if (!theError)
|
||
theError = AccuAddFromHandle (a, value, offset, spot - *value - offset);
|
||
if (!theError)
|
||
theError = AccuAddStr (a, "\p=0D=0A");
|
||
if (!theError && spot + 1 < end)
|
||
theError = AccuAddStr (a, "\p=\r\n");
|
||
offset = spot - *value + 1;
|
||
count = 0;
|
||
break;
|
||
}
|
||
++spot;
|
||
++count;
|
||
}
|
||
if (!theError)
|
||
theError = AccuAddFromHandle (a, value, offset, end - *value - offset);
|
||
UL (value);
|
||
return (theError);
|
||
}
|
||
|
||
|
||
static OSErr VCardAccuMaybeQuotedPrintableSoftline (AccuPtr a, Handle value, long *offset, long *count, long maxChar)
|
||
|
||
{
|
||
OSErr theError;
|
||
|
||
theError = noErr;
|
||
if (*count >= maxChar) {
|
||
theError = AccuAddFromHandle (a, value, *offset, maxChar);
|
||
if (!theError)
|
||
theError = AccuAddStr (a, "\p=\r\n");
|
||
*offset += maxChar;
|
||
*count = 0;
|
||
}
|
||
return (theError);
|
||
}
|
||
|
||
|
||
static Boolean VCardValueContainsCRLFs (Handle value)
|
||
|
||
{
|
||
Ptr spot;
|
||
Size length;
|
||
|
||
if (value) {
|
||
length = GetHandleSize (value);
|
||
for (spot = *value; spot < *value + length; spot++)
|
||
if (*spot == '\r' || *spot == '\n')
|
||
return (true);
|
||
}
|
||
return (false);
|
||
}
|
||
|
||
|
||
static OSErr VCardAccuAddProperties (AccuPtr a, short count, short *vcProp)
|
||
|
||
{
|
||
Str255 property;
|
||
OSErr theError;
|
||
short i;
|
||
|
||
theError = noErr;
|
||
for (i = 0; i < count && !theError; ++i) {
|
||
theError = AccuAddStr (a, GetRString (property, VCardKeywordStrn + vcProp[i]));
|
||
if (!theError && i + 1 < count)
|
||
theError = AccuAddChar (a, ';');
|
||
}
|
||
return (theError);
|
||
}
|
||
|
||
static OSErr VCardAccuAddPropertiesAndValue (AccuPtr a, short count, short *vcProp, Handle value, Boolean quotedPrintable)
|
||
|
||
{
|
||
OSErr theError;
|
||
|
||
if (value) {
|
||
theError = VCardAccuAddProperties (a, count, vcProp);
|
||
if (!theError)
|
||
theError = AccuAddChar (a, ':');
|
||
if (!theError)
|
||
theError = VCardAccuAddValue (a, value, quotedPrintable);
|
||
if (!theError)
|
||
theError = AccuAddStr (a, "\p\r\n");
|
||
}
|
||
return (theError);
|
||
}
|
||
|
||
|
||
//
|
||
// VCardAccuAddValueList
|
||
//
|
||
// Add a value list to the vCard data accumulator.
|
||
//
|
||
|
||
static OSErr VCardAccuAddValueList (AccuPtr a, Handle notes, short count, short *abTag, Boolean *anyValues)
|
||
|
||
{
|
||
Str255 property,
|
||
tag;
|
||
Handle **values,
|
||
*valuePtr;
|
||
OSErr theError;
|
||
short i;
|
||
Boolean weHaveValue,
|
||
quotedPrintable;
|
||
|
||
weHaveValue = false;
|
||
quotedPrintable = false;
|
||
|
||
// Allocate space for one Handle per requested value
|
||
values = NuHandleClear (count * sizeof (Handle));
|
||
theError = MemError ();
|
||
|
||
// Get the values
|
||
valuePtr = LDRef (values);
|
||
for (i = 0; i < count && !theError; ++i, ++valuePtr) {
|
||
if (abTag[i])
|
||
if (*valuePtr = GetTaggedFieldValueInNotes (notes, GetRString (tag, ABReservedTagsStrn + abTag[i]))) {
|
||
weHaveValue = true;
|
||
if (!quotedPrintable)
|
||
quotedPrintable = VCardValueContainsCRLFs (*valuePtr);
|
||
}
|
||
theError = MemError ();
|
||
}
|
||
|
||
// Add the formatted values to the accumulator (values still locked)
|
||
if (weHaveValue) {
|
||
if (quotedPrintable) {
|
||
theError = AccuAddChar (a, ';');
|
||
if (!theError)
|
||
theError = AccuAddStr (a, GetRString (property, VCardKeywordStrn + vcQuotedPrintable));
|
||
}
|
||
if (!theError)
|
||
theError = AccuAddChar (a, ':');
|
||
valuePtr = *values;
|
||
for (i = 0; i < count && !theError; ++i, ++valuePtr) {
|
||
theError = VCardAccuAddValue (a, *valuePtr, quotedPrintable);
|
||
if (!theError && i + 1 < count)
|
||
theError = AccuAddChar (a, ';');
|
||
}
|
||
if (!theError)
|
||
theError = AccuAddStr (a, "\p\r\n");
|
||
}
|
||
|
||
// Dispose of the memory we allocated
|
||
if (values) {
|
||
valuePtr = *values;
|
||
for (i = 0; i < count; ++i)
|
||
ZapHandle (*valuePtr);
|
||
ZapHandle (values);
|
||
}
|
||
|
||
if (anyValues)
|
||
*anyValues = weHaveValue;
|
||
return (theError);
|
||
}
|
||
|
||
|
||
static OSErr VCardAccuAddAVPairStr (AccuPtr a, short attributeResIndex, PStr value)
|
||
|
||
{
|
||
Str255 scratch;
|
||
OSErr theError;
|
||
|
||
theError = AccuAddStr (a, GetRString (scratch, VCardKeywordStrn + attributeResIndex));
|
||
if (!theError)
|
||
theError = AccuAddChar (a, ':');
|
||
if (!theError)
|
||
theError = AccuAddStr (a, value);
|
||
if (!theError)
|
||
theError = AccuAddStr (a, "\p\r\n");
|
||
return (theError);
|
||
}
|
||
|
||
|
||
static OSErr VCardAccuAddAVPairRes (AccuPtr a, short attributeResIndex, short valueResIndex)
|
||
|
||
{
|
||
Str255 scratch;
|
||
|
||
return (VCardAccuAddAVPairStr (a, attributeResIndex, GetRString (scratch, VCardKeywordStrn + valueResIndex)));
|
||
}
|
||
|
||
|
||
static OSErr VCardAccuAddAddress (AccuPtr a, Handle notes, Boolean home, Boolean preferred)
|
||
|
||
{
|
||
OSErr theError;
|
||
long oldOffset;
|
||
short abTag[kVCardLineMaxValues], // More than enough room for now
|
||
vcProp[kVCardLineMaxValues], // More than enough room for now
|
||
count;
|
||
Boolean anyValues;
|
||
|
||
// Home Address
|
||
oldOffset = a->offset;
|
||
vcProp[0] = vcAdr;
|
||
vcProp[1] = home ? vcHome : vcWork;
|
||
count = 2;
|
||
if (preferred)
|
||
vcProp[count++] = vcPref;
|
||
theError = VCardAccuAddProperties (a, count, vcProp);
|
||
if (!theError) {
|
||
abTag[0] = abTag[1] = 0;
|
||
if (home) {
|
||
abTag[2] = abTagAddress;
|
||
abTag[3] = abTagCity;
|
||
abTag[4] = abTagState;
|
||
abTag[5] = abTagZip;
|
||
abTag[6] = abTagCountry;
|
||
}
|
||
else {
|
||
abTag[2] = abTagAddress2;
|
||
abTag[3] = abTagCity2;
|
||
abTag[4] = abTagState2;
|
||
abTag[5] = abTagZip2;
|
||
abTag[6] = abTagCountry2;
|
||
}
|
||
theError = VCardAccuAddValueList (a, notes, 7, abTag, &anyValues);
|
||
}
|
||
if (!anyValues)
|
||
a->offset = oldOffset;
|
||
return (theError);
|
||
}
|
||
|
||
static OSErr VCardAccuAddPhone (AccuPtr a, Handle notes, short propertyID, short propertyLocation, short tagID, Boolean preferred)
|
||
|
||
{
|
||
Str255 tag;
|
||
Handle value;
|
||
OSErr theError;
|
||
short vcProp[kVCardLineMaxValues], // More than enough room for now
|
||
count;
|
||
Boolean quotedPrintable;
|
||
|
||
theError = noErr;
|
||
if (value = GetTaggedFieldValueInNotes (notes, GetRString (tag, ABReservedTagsStrn + tagID))) {
|
||
quotedPrintable = VCardValueContainsCRLFs (value);
|
||
vcProp[0] = vcTel;
|
||
vcProp[1] = propertyID;
|
||
vcProp[2] = propertyLocation;
|
||
count = 3;
|
||
if (preferred)
|
||
vcProp[count++] = vcPref;
|
||
if (quotedPrintable)
|
||
vcProp[count++] = vcQuotedPrintable;
|
||
theError = VCardAccuAddPropertiesAndValue (a, count, vcProp, value, quotedPrintable);
|
||
ZapHandle (value);
|
||
}
|
||
return (theError);
|
||
}
|
||
|
||
static OSErr VCardAccuAddURL (AccuPtr a, Handle notes, short propertyLocation, short tagID, Boolean preferred)
|
||
|
||
{
|
||
Str255 tag;
|
||
Handle value;
|
||
OSErr theError;
|
||
short vcProp[kVCardLineMaxValues], // More than enough room for now
|
||
count;
|
||
Boolean quotedPrintable;
|
||
|
||
theError = noErr;
|
||
if (value = GetTaggedFieldValueInNotes (notes, GetRString (tag, ABReservedTagsStrn + tagID))) {
|
||
quotedPrintable = VCardValueContainsCRLFs (value);
|
||
vcProp[0] = vcURL;
|
||
vcProp[1] = propertyLocation;
|
||
count = 2;
|
||
if (preferred)
|
||
vcProp[count++] = vcPref;
|
||
if (quotedPrintable)
|
||
vcProp[count++] = vcQuotedPrintable;
|
||
theError = VCardAccuAddPropertiesAndValue (a, count, vcProp, value, quotedPrintable);
|
||
ZapHandle (value);
|
||
}
|
||
return (theError);
|
||
}
|
||
|
||
|
||
static OSErr VCardAccuAddOtherEmail (AccuPtr a, Handle notes)
|
||
|
||
{
|
||
BinAddrHandle addresses;
|
||
Str255 scratch;
|
||
Handle value;
|
||
Ptr addrPtr;
|
||
OSErr theError;
|
||
|
||
theError = noErr;
|
||
if (value = GetTaggedFieldValueInNotes (notes, GetRString (scratch, ABReservedTagsStrn + abTagOtherEmail))) {
|
||
if (!SuckAddresses (&addresses, value, false, true, false, nil)) {
|
||
// Create an 'EMAIL' line for each address we suck from the other email field
|
||
for (addrPtr = LDRef (addresses); !theError && *addrPtr; addrPtr += *addrPtr + 2)
|
||
theError = VCardAccuAddAVPairStr (a, vcEmail, addrPtr);
|
||
}
|
||
ZapHandle (addresses);
|
||
ZapHandle (value);
|
||
}
|
||
return (theError);
|
||
}
|
||
|
||
|
||
static OSErr VCardAccuAddOther (AccuPtr a, Handle notes, short propertyID, short tagID)
|
||
|
||
{
|
||
Str255 tag;
|
||
Handle value;
|
||
OSErr theError;
|
||
short vcProp[kVCardLineMaxValues], // More than enough room for now
|
||
count;
|
||
Boolean quotedPrintable;
|
||
|
||
theError = noErr;
|
||
if (value = GetTaggedFieldValueInNotes (notes, GetRString (tag, ABReservedTagsStrn + tagID))) {
|
||
quotedPrintable = VCardValueContainsCRLFs (value);
|
||
vcProp[0] = propertyID;
|
||
count = 1;
|
||
if (quotedPrintable)
|
||
vcProp[count++] = vcQuotedPrintable;
|
||
theError = VCardAccuAddPropertiesAndValue (a, count, vcProp, value, quotedPrintable);
|
||
ZapHandle (value);
|
||
}
|
||
return (theError);
|
||
}
|
||
|
||
|
||
static OSErr VCardAccuAddNotes (AccuPtr a, Handle notes)
|
||
|
||
{
|
||
AttributeValueHandle avPairs;
|
||
Str255 tag;
|
||
Handle value,
|
||
leftovers;
|
||
OSErr theError;
|
||
short vcProp[kVCardLineMaxValues], // More than enough room for now
|
||
count;
|
||
Boolean quotedPrintable;
|
||
|
||
theError = noErr;
|
||
leftovers = nil;
|
||
value = GetTaggedFieldValueInNotes (notes, GetRString (tag, ABReservedTagsStrn + abTagNote));
|
||
avPairs = ParseAllAttributeValuePairs (notes, &leftovers, avAllButHiddenPairs, avPairUnknown);
|
||
if (leftovers) {
|
||
if (!value) {
|
||
value = NuHandle (0);
|
||
theError = MemError ();
|
||
}
|
||
else
|
||
theError = PtrPlusHand ("\r\n", value, 2);
|
||
if (!theError)
|
||
theError = HandPlusHand (leftovers, value);
|
||
}
|
||
if (value && !theError) {
|
||
quotedPrintable = VCardValueContainsCRLFs (value);
|
||
vcProp[0] = vcNote;
|
||
count = 1;
|
||
if (quotedPrintable)
|
||
vcProp[count++] = vcQuotedPrintable;
|
||
theError = VCardAccuAddPropertiesAndValue (a, count, vcProp, value, quotedPrintable);
|
||
}
|
||
ZapHandle (value);
|
||
ZapHandle (leftovers);
|
||
ZapHandle (avPairs);
|
||
return (theError);
|
||
}
|
||
|
||
|
||
//
|
||
// ParseVCard
|
||
//
|
||
// Accepts the text of a vCard and attempts to create a Eudora nickname, returning (hopefully)
|
||
// a suggested nickname, addresses and notes.
|
||
//
|
||
// If data remains after we've sucked out a vCard, we'll put the offset to that data in offset,
|
||
// and they can call us again to suck out the rest
|
||
//
|
||
|
||
OSErr ParseVCard (Handle data, uLong *offset, XDashStringHandle xDashStrings, VCardItemProc itemProc, VCardErrorProc errorProc, long refcon)
|
||
{
|
||
VCardParserRec parser;
|
||
OSErr theError;
|
||
|
||
if (!IsVCardAvailable ())
|
||
return (noErr);
|
||
|
||
UseFeature (featureVCard);
|
||
|
||
|
||
// My hero, zero
|
||
Zero (parser);
|
||
|
||
parser.data = data;
|
||
parser.spot = LDRef (data) + *offset;
|
||
parser.end = *data + GetHandleSize (data) - 1;
|
||
parser.error = vcErrorNone;
|
||
parser.xdash = xDashStrings;
|
||
parser.itemProc = itemProc;
|
||
parser.errorProc = errorProc;
|
||
parser.refcon = refcon;
|
||
theError = VCardParserVCardFile (&parser); // ...and, they're off!
|
||
|
||
if (parser.errorProc && parser.error)
|
||
(*parser.errorProc) (data, &parser.item, refcon, parser.error, parser.spot - *data);
|
||
|
||
if (parser.spot>=parser.end) *offset = 0;
|
||
else *offset = parser.spot - *data;
|
||
|
||
UL (data);
|
||
|
||
return (theError);
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParserVCardFile
|
||
//
|
||
// vcardfile = [wsls] vcard [wsls]
|
||
//
|
||
|
||
static OSErr VCardParserVCardFile (VCardParserPtr parserPtr)
|
||
|
||
{
|
||
OSErr theError;
|
||
|
||
// Check for optional whitespace and linefeeds
|
||
VCardParserWSLS (parserPtr, true);
|
||
|
||
// Process vcard data
|
||
theError = VCardParserVCard (parserPtr);
|
||
|
||
// Check for more optional whitespace and linefeeds
|
||
if (!theError && parserPtr->error == vcErrorNone)
|
||
VCardParserWSLS (parserPtr, true);
|
||
|
||
return (theError);
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParserVCard
|
||
//
|
||
// vcard = "BEGIN:VCARD" [ws 7bit] 1*CRLF items *CRLF "END:VCARD"
|
||
//
|
||
|
||
static OSErr VCardParserVCard (VCardParserPtr parserPtr)
|
||
|
||
{
|
||
Str255 keyword,
|
||
scratch;
|
||
OSErr theError;
|
||
|
||
// Initialize the handles we'll be needing
|
||
parserPtr->item.value = NuHandle (0);
|
||
parserPtr->item.group = NuHandle (0);
|
||
parserPtr->item.params = NuHandle (0);
|
||
parserPtr->item.strings = NuHandle (0);
|
||
theError = MemError ();
|
||
|
||
// Check for "BEGIN:VCARD"
|
||
if (!StringSame (VCardParserKeyword (parserPtr, keyword), GetRString (scratch, VCardKeywordStrn + vcBegin)))
|
||
parserPtr->error = vcMissingBegin;
|
||
if (parserPtr->error == vcErrorNone)
|
||
if (VCardParserCharacter (parserPtr, ':', false))
|
||
if (!StringSame (VCardParserKeyword (parserPtr, keyword), GetRString (scratch, VCardKeywordStrn + vcVCard)))
|
||
parserPtr->error = vcMissingVcard;
|
||
|
||
// Check for the optional [ws 7bit] portion
|
||
if (!theError && parserPtr->error == vcErrorNone)
|
||
if (VCardParserWS (parserPtr, true))
|
||
theError = VCardParser7BitValue (parserPtr, nil, false, false);
|
||
|
||
// Skip over 1 or more CRLF's
|
||
if (!theError && parserPtr->error == vcErrorNone)
|
||
VCardParseCRLFs (parserPtr, false);
|
||
|
||
// Process the 'items' in the vCard
|
||
if (!theError && parserPtr->error == vcErrorNone)
|
||
theError = VCardParserItems (parserPtr);
|
||
|
||
// Check for "END:VCARD" (actually, check for ":VCARD" since we already parsed the keyword as an item)
|
||
if (!theError) {
|
||
if (parserPtr->error == vcErrorNone)
|
||
if (parserPtr->item.property != vcKeyEnd)
|
||
parserPtr->error = vcMissingEnd;
|
||
if (parserPtr->error == vcErrorNone)
|
||
if (VCardParserCharacter (parserPtr, ':', false))
|
||
if (!StringSame (VCardParserKeyword (parserPtr, keyword), GetRString (scratch, VCardKeywordStrn + vcVCard)))
|
||
parserPtr->error = vcMissingVcard;
|
||
}
|
||
|
||
// Release our memory
|
||
ZapHandle (parserPtr->item.value);
|
||
ZapHandle (parserPtr->item.group);
|
||
ZapHandle (parserPtr->item.params);
|
||
ZapHandle (parserPtr->item.strings);
|
||
|
||
return (theError);
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParserItems
|
||
//
|
||
// items = items *CRLF item
|
||
// / item
|
||
//
|
||
// This effectively means 1 or more items. We'll stop once we've
|
||
// parsed "END" as the item property.
|
||
//
|
||
|
||
static OSErr VCardParserItems (VCardParserPtr parserPtr)
|
||
|
||
{
|
||
OSErr theError;
|
||
short numItems;
|
||
|
||
theError = noErr;
|
||
numItems = 0;
|
||
do {
|
||
theError = VCardParserItem (parserPtr);
|
||
|
||
// Error handling
|
||
if (parserPtr->error) {
|
||
// Callback to the client and quit if the client found the error Super Serious
|
||
if (parserPtr->errorProc)
|
||
if ((*parserPtr->errorProc) (parserPtr->data, &parserPtr->item, parserPtr->refcon, parserPtr->error, parserPtr->spot - *parserPtr->data))
|
||
break;
|
||
|
||
// Clear the error for the next item and skip to the next line in the data
|
||
parserPtr->error = vcErrorNone;
|
||
}
|
||
else
|
||
if (!theError)
|
||
++numItems;
|
||
|
||
} while (!theError && parserPtr->item.property != vcKeyEnd && parserPtr->spot <= parserPtr->end);
|
||
|
||
// Odd case, but we need it
|
||
if (numItems && parserPtr->error == vcErrorExpectingItem)
|
||
parserPtr->error = vcErrorNone;
|
||
|
||
return (theError);
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParserItem
|
||
//
|
||
// item = [ws] [groups "."] name [params] ":" value CRLF
|
||
// / [ws] [groups "."] "ADR" [params] ":" addressparts CRLF
|
||
// / [ws] [groups "."] "ORG" [params] ":" orgparts CRLF
|
||
// / [ws] [groups "."] "N" [params] ":" nameparts CRLF
|
||
// / [ws] [groups "."] "AGENT" [params] ":" vcard CRLF
|
||
//
|
||
// In parsing the item information we're going to make a couple of assumptions about
|
||
// what constitutes an "item". As we parse the text of a line, we might be looking
|
||
// at a group, or a name, or a known type. Each case shares a certain format symetry.
|
||
//
|
||
// text [paramList] delimiter value CRLF
|
||
//
|
||
// Notice that a 'group' is nothing more than a series of word separated by periods --
|
||
// and notice that 'params' is a list of parameters, each preceded by a semicolon.
|
||
// Therefore, we can safely (ha!) assume that we can determine the meaning of the text
|
||
// based on the type of delimiter we find.
|
||
//
|
||
// As we successfully parse the line items of a vCard, we build a VCardItemRec
|
||
// which gets passed back to the client in a callback. The client can then
|
||
// pick values, fields, or whatever from this structure in neat, tidy keywords.
|
||
//
|
||
|
||
static OSErr VCardParserItem (VCardParserPtr parserPtr)
|
||
|
||
{
|
||
Str255 keyword;
|
||
OSErr theError;
|
||
|
||
// Initial values for the item record
|
||
parserPtr->item.property = vcKeyNone;
|
||
parserPtr->item.encoding = vcValue7Bit;
|
||
parserPtr->item.charset = vcValueUSAscii;
|
||
parserPtr->item.propertyFlags = vcPropFlagNone;
|
||
SetHandleBig (parserPtr->item.value, 0);
|
||
SetHandleBig (parserPtr->item.group, 0);
|
||
SetHandleBig (parserPtr->item.params, 0);
|
||
SetHandleBig (parserPtr->item.strings, 0);
|
||
|
||
// Check for optional whitespace
|
||
VCardParserWS (parserPtr, true);
|
||
|
||
// Check for the presence of grouping information
|
||
theError = VCardParserGroups (parserPtr, &parserPtr->item, keyword);
|
||
|
||
// At this point, 'keyword' contains primary property (the last parsed keyword prior to the delimiter)
|
||
if (!theError && !parserPtr->error)
|
||
parserPtr->item.property = VCardProcessProperty (parserPtr, keyword);
|
||
|
||
// We're done if we've found the "END" property (and it's not some stupidly ill-formed group syntax)
|
||
if (!theError && !parserPtr->error && parserPtr->item.property == vcKeyEnd)
|
||
return (theError);
|
||
|
||
// Do we have a parameter list to parse?
|
||
if (!theError && !parserPtr->error && VCardParserCharacter (parserPtr, ';', true))
|
||
theError = VCardParserParameters (parserPtr, &parserPtr->item);
|
||
|
||
// Do we have a value to parse?
|
||
if (!theError && !parserPtr->error && VCardParserCharacter (parserPtr, ':', false))
|
||
theError = VCardParserValue (parserPtr, &parserPtr->item);
|
||
|
||
// No errors? Let the client process the results of this item
|
||
if (!theError && parserPtr->item.property != vcKeyNone && parserPtr->itemProc)
|
||
theError = (*parserPtr->itemProc) (&parserPtr->item, parserPtr->refcon);
|
||
|
||
// Skip over exactly 1 CRLF or to the beginning of the next line if an error was detected
|
||
if (!theError && parserPtr->error == vcErrorNone)
|
||
VCardParseCRLFs (parserPtr, false);
|
||
else
|
||
VCardParserSkipToNextLine (parserPtr);
|
||
return (theError);
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParserGroups
|
||
//
|
||
// groups = groups "." word
|
||
// / word
|
||
//
|
||
// Loop looking for keywords that precede a "." and append these to the item's group handle.
|
||
// The function concludes when there's no more text to read, or when we've hit a delimiter
|
||
// that is NOT a period.
|
||
//
|
||
// Upon leaving the function, 'spot' points to the terminating delimiter (so it should be
|
||
// either a ':' or a ';' if the vCard is valid -- and if we cared since right now we're not
|
||
// exactly a validating parser.
|
||
//
|
||
|
||
static OSErr VCardParserGroups (VCardParserPtr parserPtr, VCardItemPtr itemPtr, Str255 keyword)
|
||
|
||
{
|
||
OSErr theError;
|
||
Boolean done;
|
||
|
||
theError = noErr;
|
||
|
||
done = false;
|
||
while (parserPtr->spot <= parserPtr->end && !done && !theError && !parserPtr->error) {
|
||
// Grab a keyword
|
||
VCardParserKeyword (parserPtr, keyword);
|
||
|
||
// If the delimiter is a '.' then the preceding keyword was part of a grouping... append it!
|
||
if (*parserPtr->spot == '.')
|
||
theError = VCardAppendKeywordToGroup (itemPtr, keyword);
|
||
done = !VCardParserCharacter (parserPtr, '.', true);
|
||
}
|
||
return (theError);
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParserParameters
|
||
//
|
||
// paramlist = paramlist [ws] ";" [ws] param
|
||
// / param
|
||
//
|
||
// This effectively means 1 param, or a sequence of params separated by semicolons.
|
||
//
|
||
// param = "TYPE" [ws] "=" [ws] ptypeval
|
||
// / "VALUE" [ws] "=" [ws] pvalueval
|
||
// / "ENCODING" [ws] "=" [ws] pencodingval
|
||
// / "CHARSET" [ws] "=" [ws] charsetval
|
||
// / "LANGUAGE" [ws] "=" [ws] langval
|
||
// / "X-" word [ws] "=" [ws] word
|
||
// / knowntype
|
||
//
|
||
// Upon entry, 'spot' should be pointing to the first character in the parameter list.
|
||
// On exit, 'spot' should point to the first character following the parameter list.
|
||
//
|
||
|
||
static OSErr VCardParserParameters (VCardParserPtr parserPtr, VCardItemPtr itemPtr)
|
||
|
||
{
|
||
VCardParamRec param;
|
||
Str255 keyword;
|
||
OSErr theError;
|
||
Boolean done;
|
||
|
||
theError = noErr;
|
||
SetHandleBig (itemPtr->strings, 0);
|
||
|
||
// Loop-ity-loop until we are done.
|
||
done = false;
|
||
while (parserPtr->spot <= parserPtr->end && !theError && !done && !parserPtr->error) {
|
||
// My hero, zero
|
||
Zero (param);
|
||
|
||
// Parse a property as a keyword and examine it closely
|
||
switch (param.pProperty = VCardProcessProperty (parserPtr, VCardParserKeyword (parserPtr, keyword))) {
|
||
case vcKeyNone:
|
||
parserPtr->error = vcErrorUnknownParamter;
|
||
done = true;
|
||
break;
|
||
case vcKeyType:
|
||
VCardParserEqualSign (parserPtr);
|
||
if (parserPtr->error == vcErrorNone)
|
||
VCardParserParameterTypeValue (parserPtr, ¶m);
|
||
break;
|
||
case vcKeyValue:
|
||
VCardParserEqualSign (parserPtr);
|
||
if (parserPtr->error == vcErrorNone)
|
||
VCardParserParameterValueValue (parserPtr, ¶m);
|
||
break;
|
||
case vcKeyEncoding:
|
||
VCardParserEqualSign (parserPtr);
|
||
if (parserPtr->error == vcErrorNone)
|
||
VCardParserParameterEncodingValue (parserPtr, ¶m);
|
||
break;
|
||
case vcKeyCharset:
|
||
VCardParserEqualSign (parserPtr);
|
||
if (parserPtr->error == vcErrorNone)
|
||
VCardParserParameterCharsetValue (parserPtr, ¶m);
|
||
break;
|
||
case vcKeyLanguage:
|
||
VCardParserEqualSign (parserPtr);
|
||
if (parserPtr->error == vcErrorNone)
|
||
VCardParserParameterLangaugeValue (parserPtr, ¶m);
|
||
break;
|
||
case vcKey7Bit:
|
||
case vcKey8Bit:
|
||
case vcKeyQuotedPrintable:
|
||
case vcKeyBase64:
|
||
// Annoyingly, we have to do this for vCards that don't follow the spec.
|
||
VCardParserProcessOrphanedEncoding (itemPtr, param.pProperty);
|
||
break;
|
||
case vcKeyHome:
|
||
itemPtr->propertyFlags |= vcPropFlagHome;
|
||
break;
|
||
case vcKeyWork:
|
||
itemPtr->propertyFlags |= vcPropFlagWork;
|
||
break;
|
||
case vcKeyPref:
|
||
itemPtr->propertyFlags |= vcPropFlagPreferred;
|
||
break;
|
||
default:
|
||
// Was an "X-" found as the parameter property?
|
||
if (param.pProperty < 0) {
|
||
VCardParserEqualSign (parserPtr);
|
||
if (parserPtr->error == vcErrorNone)
|
||
theError = VCardParserAppendPropertyValueString (parserPtr, ¶m, itemPtr->strings);
|
||
}
|
||
// else the pProperty was a knowntype
|
||
break;
|
||
}
|
||
|
||
// Append the parameter record onto our Big Block of Parameters
|
||
if (!theError && !parserPtr->error)
|
||
theError = PtrPlusHand (¶m, itemPtr->params, sizeof (param));
|
||
|
||
done = !VCardParserCharacter (parserPtr, ';', true);
|
||
}
|
||
|
||
// Done looping... we SHOULD be pointing at a colon (which means a value is upcoming -- oooo! ahhh!)
|
||
return (theError);
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParserParameterTypeValue
|
||
//
|
||
// ptypeval = knowntype
|
||
// / "X-" word
|
||
//
|
||
|
||
static void VCardParserParameterTypeValue (VCardParserPtr parserPtr, VCardParamPtr paramPtr)
|
||
|
||
{
|
||
Str255 keyword;
|
||
|
||
paramPtr->pValue = VCardProcessProperty (parserPtr, VCardParserKeyword (parserPtr, keyword));
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParserParameterValueValue
|
||
//
|
||
// pvalueval = "INLINE"
|
||
// / "URL"
|
||
// / "CONTENT-ID"
|
||
// / "X-" word
|
||
//
|
||
// Functionality is the same as above for now, but these are broken out into separate
|
||
// functions just in case we want the parser to do some keyword validation down the road.
|
||
//
|
||
|
||
static void VCardParserParameterValueValue (VCardParserPtr parserPtr, VCardParamPtr paramPtr)
|
||
|
||
{
|
||
Str255 keyword;
|
||
|
||
paramPtr->pValue = VCardProcessProperty (parserPtr, VCardParserKeyword (parserPtr, keyword));
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParserParameterEncodingValue
|
||
//
|
||
// pencodingval = "7BIT"
|
||
// / "8BIT"
|
||
// / "QUOTED-PRINTABLE"
|
||
// / "BASE64"
|
||
// / "X-" word
|
||
//
|
||
|
||
static void VCardParserParameterEncodingValue (VCardParserPtr parserPtr, VCardParamPtr paramPtr)
|
||
|
||
{
|
||
Str255 keyword;
|
||
|
||
VCardParserProcessOrphanedEncoding (&parserPtr->item, paramPtr->pValue = VCardProcessProperty (parserPtr, VCardParserKeyword (parserPtr, keyword)));
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParserParameterCharsetValue
|
||
//
|
||
// charsetval = <a character set string as defined in section 7.1 of RFC 1521>
|
||
//
|
||
// We'll parse a keyword and see if it matches a known charset value.
|
||
//
|
||
|
||
static void VCardParserParameterCharsetValue (VCardParserPtr parserPtr, VCardParamPtr paramPtr)
|
||
|
||
{
|
||
Str255 keyword,
|
||
scratch;
|
||
short value,
|
||
i;
|
||
|
||
VCardParserKeyword (parserPtr, keyword);
|
||
|
||
// US-ASCII?
|
||
if (StringSame (VCardParserKeyword (parserPtr, keyword), GetRString (scratch, VCardKeywordStrn + vcVCard)))
|
||
parserPtr->item.charset = vcValueUSAscii;
|
||
else
|
||
// How about ISO-8859?
|
||
if (BeginsWith (keyword, GetRString (scratch, VCardKeywordStrn + vcISO_8859))) {
|
||
value = 0;
|
||
for (i = scratch[0] + 1; i <= keyword[0]; ++i)
|
||
value = value * 10 + (keyword[i] - '0');
|
||
parserPtr->item.charset = vcValueISO_8859_1 + value - 1;
|
||
}
|
||
else
|
||
parserPtr->item.charset = vcValueOtherCharset;
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParserParameterLangaugeValue
|
||
//
|
||
// langval = <a language string as defined in RFC 1766>
|
||
// = 1*8ALPHA *( "-" 1*8ALPHA )
|
||
//
|
||
// Were going to be extremely lax here and just parse this as a regular value, leaving it up to
|
||
// the caller to make sense out of the string. In most cases, this is going to be a legitimate
|
||
// 2 character language code, but we may sometimes see subtags. Oh well.
|
||
//
|
||
|
||
static void VCardParserParameterLangaugeValue (VCardParserPtr parserPtr, VCardParamPtr paramPtr)
|
||
|
||
{
|
||
(void) VCardParserAppendPropertyValueString (parserPtr, paramPtr, parserPtr->item.strings);
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParserProcessOrphanedEncoding
|
||
//
|
||
// The only reason why this function exists is because of existing vCard writers that do not
|
||
// propery follow the spec. Encodings are not supposed to appear as standalone properties,
|
||
// a la ...;WORK;QUOTED-PRINTABLE:... They are supposed to be matched with the ENCODING
|
||
// keyword a la ...;WORK;ENCODING=QUOTED-PRINTABLE:...
|
||
//
|
||
// I understand the rationale, but c'mon... follow the spec.
|
||
//
|
||
|
||
static void VCardParserProcessOrphanedEncoding (VCardItemPtr itemPtr, vcKeywordType encodingValue)
|
||
|
||
{
|
||
switch (encodingValue) {
|
||
case vcKey7Bit:
|
||
itemPtr->encoding = vcValue7Bit;
|
||
break;
|
||
case vcKey8Bit:
|
||
itemPtr->encoding = vcValue8Bit;
|
||
break;
|
||
case vcKeyQuotedPrintable:
|
||
itemPtr->encoding = vcValueQuotedPrintable;
|
||
break;
|
||
case vcKeyBase64:
|
||
itemPtr->encoding = vcValueBase64;
|
||
break;
|
||
default:
|
||
// Must handle the "X-" case somehow
|
||
itemPtr->encoding = vcValueOtherEncoding;
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// VCardParserEqualSign
|
||
//
|
||
// [ws] "=" [ws]
|
||
//
|
||
// Used often enough to be broken out into a function.
|
||
//
|
||
|
||
static void VCardParserEqualSign (VCardParserPtr parserPtr)
|
||
|
||
{
|
||
// Check for optional whitespace...
|
||
VCardParserWS (parserPtr, true);
|
||
|
||
// ...and the required '=' sign
|
||
VCardParserCharacter (parserPtr, '=', false);
|
||
|
||
// Check for optional whitespace...
|
||
if (parserPtr->error == vcErrorNone)
|
||
VCardParserWS (parserPtr, true);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// VCardParserValue
|
||
//
|
||
// At this point, 'spot' should be pointing to the first character of an item's
|
||
// value. This function attempts to act on the item's stored property, like so:
|
||
//
|
||
// "ADR" -- Parse the address parts
|
||
// "ORG" -- Parse the organization parts
|
||
// "N" -- Parse the name parts
|
||
// "AGENT" -- An abomination!
|
||
// other -- Just stores all following characters in the item's 'value' handle.
|
||
//
|
||
//
|
||
|
||
static OSErr VCardParserValue (VCardParserPtr parserPtr, VCardItemPtr itemPtr)
|
||
|
||
{
|
||
OSErr theError;
|
||
|
||
theError = noErr;
|
||
|
||
// Do something cool depending on the property
|
||
switch (itemPtr->property) {
|
||
case vcKeyAdr:
|
||
theError = VCardParserAddress (parserPtr, itemPtr);
|
||
break;
|
||
case vcKeyOrg:
|
||
theError = VCardParserOrganization (parserPtr, itemPtr);
|
||
break;
|
||
case vcKeyN:
|
||
theError = VCardParserName (parserPtr, itemPtr);
|
||
break;
|
||
case vcKeyAgent:
|
||
// The value will contain an entire vCard... do we parse this, or just pass it back???
|
||
// We certainly could parse it, of course, but we would need to tell the caller that
|
||
// this data coming back will be different than that we are currently parsing
|
||
break;
|
||
default:
|
||
switch (itemPtr->encoding) {
|
||
case vcValue7Bit:
|
||
theError = VCardParser7BitValue (parserPtr, itemPtr, false, false);
|
||
break;
|
||
case vcValue8Bit:
|
||
theError = VCardParser8BitValue (parserPtr, itemPtr);
|
||
break;
|
||
case vcValueQuotedPrintable:
|
||
// Parse as quoted printable
|
||
theError = VCardParserQuotedPrintableValue (parserPtr, itemPtr, 0);
|
||
break;
|
||
case vcValueBase64:
|
||
// Parse as base 64
|
||
theError = VCardParserBase64Value (parserPtr, itemPtr);
|
||
break;
|
||
case vcValueOtherEncoding:
|
||
// Probably execute a callback to allow the client to interpret the value?
|
||
default:
|
||
// Just skip it...
|
||
theError = VCardParser7BitValue (parserPtr, nil, false, false);
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
return (theError);
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParserAddress
|
||
//
|
||
// addressparts = 0*6 (strnosemi ";") strnosemi
|
||
//
|
||
// PO Box, Extended Addr, Street, Locality, Region, Postal Code, Country Name
|
||
//
|
||
// For each part of the address, we want to parse until we reach a semicolon, put
|
||
// the found text into 'value', set the proper property keyword, then callback the
|
||
// results to the client.
|
||
//
|
||
|
||
static OSErr VCardParserAddress (VCardParserPtr parserPtr, VCardItemPtr itemPtr)
|
||
|
||
{
|
||
vcKeywordType addrParts[7] = { vcPOBox, vcExtendedAddress, vcStreet, vcLocality,
|
||
vcRegion, vcPostalCode, vcCountry };
|
||
|
||
return (VCardParserPropertPartList (parserPtr, itemPtr, addrParts, 7));
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParserOrganization
|
||
//
|
||
// orgparts = * (strnosemi ";") strnosemi
|
||
//
|
||
// Organization Name, Organization Units
|
||
//
|
||
// For each part of the organization, we want to parse until we reach a semicolon, put
|
||
// the found text into 'value', set the proper property keyword, then callback the
|
||
// results to the client.
|
||
//
|
||
|
||
static OSErr VCardParserOrganization (VCardParserPtr parserPtr, VCardItemPtr itemPtr)
|
||
|
||
{
|
||
vcKeywordType orgParts[2] = { vcOrganizationName, vcOrganizationUnits };
|
||
|
||
return (VCardParserPropertPartList (parserPtr, itemPtr, orgParts, 2));
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParserName
|
||
//
|
||
// nameparts = 0*4 (strnosemi ";") strnosemi
|
||
//
|
||
// Family, Given, Middle, Prefix, Suffix
|
||
//
|
||
// For each part of the name, we want to parse until we reach a semicolon, put
|
||
// the found text into 'value', set the proper property keyword, then callback the
|
||
// results to the client.
|
||
//
|
||
|
||
static OSErr VCardParserName (VCardParserPtr parserPtr, VCardItemPtr itemPtr)
|
||
|
||
{
|
||
vcKeywordType nameParts[5] = { vcFamilyName, vcGivenName, vcMiddleName, vcNamePrefix, vcNameSuffix };
|
||
|
||
return (VCardParserPropertPartList (parserPtr, itemPtr, nameParts, 5));
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParserPropertPartList
|
||
//
|
||
// Parse a series of semicolon delimited values from a vcard. We pass in a pointer to the
|
||
// list of parts (for example, family name, given name, etc) and the number of parts in the
|
||
// list. Each part represents a positional value, thus missing parts are still represented
|
||
// by a semicolon.
|
||
//
|
||
// This function parses until we reach a semicolon, puts the found text into item 'value',
|
||
// sets the proper property keyword (based on the part list), then calls back to the client
|
||
// to hand off the results.
|
||
//
|
||
|
||
static OSErr VCardParserPropertPartList (VCardParserPtr parserPtr, VCardItemPtr itemPtr, vcKeywordType *partList, short parts)
|
||
|
||
{
|
||
OSErr theError;
|
||
short index;
|
||
|
||
theError = noErr;
|
||
// Parse for semicolon separated strings
|
||
index = 0;
|
||
do {
|
||
// Make sure the 'value' is cleared for each part of the address
|
||
SetHandleBig (itemPtr->value, 0);
|
||
itemPtr->property = *partList++;
|
||
theError = VCardParserStrnosemi (parserPtr, itemPtr);
|
||
if (!theError && parserPtr->itemProc)
|
||
theError = (*parserPtr->itemProc)(itemPtr, parserPtr->refcon);
|
||
if (!theError)
|
||
VCardParserCharacter (parserPtr, ';', true);
|
||
} while (!theError && ++index < parts);
|
||
|
||
// Since we handled callbacks ourself, clear the property before returning
|
||
itemPtr->property = vcKeyNone;
|
||
return (theError);
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParser7BitValue
|
||
//
|
||
// 7bit = <7bit us-ascii printable chars, excluding CR LF>
|
||
//
|
||
// Parse 7bit vCard data for a value terminated by either a semicolon (in the case of
|
||
// a property value list) or a CRLF. We also handle soft returns in quote-printable text
|
||
// by passing true for 'quotedPrintable'.
|
||
//
|
||
// Pass nil for the 'value' if you don't care to actually save the value
|
||
//
|
||
|
||
static OSErr VCardParser7BitValue (VCardParserPtr parserPtr, VCardItemPtr itemPtr, Boolean valueList, Boolean quotedPrintable)
|
||
|
||
{
|
||
Ptr oldSpot;
|
||
OSErr theError;
|
||
Boolean done;
|
||
|
||
theError = noErr;
|
||
|
||
oldSpot = parserPtr->spot;
|
||
done = false;
|
||
while (!done && !theError && parserPtr->spot <= parserPtr->end) {
|
||
// Take a look at interesting characters
|
||
switch (*parserPtr->spot) {
|
||
case '\\':
|
||
// Escape semicolons
|
||
if (parserPtr->spot + 1 <= parserPtr->end && isSEMI (*(parserPtr->spot + 1))) {
|
||
// Copy the value up to (but not including) the '\'
|
||
if (itemPtr && itemPtr->value)
|
||
theError = PtrPlusHand (oldSpot, itemPtr->value, parserPtr->spot - oldSpot);
|
||
// Move the pointer to the semicolon and save this spot
|
||
oldSpot = ++parserPtr->spot;
|
||
}
|
||
break;
|
||
|
||
case ';':
|
||
// Semicolon terminates each member of a value list
|
||
if (valueList)
|
||
done = true;
|
||
break;
|
||
|
||
case '=':
|
||
// Is it just an equal sign, or a quoted-printable soft return?
|
||
if (parserPtr->spot + 1 <= parserPtr->end && isCRLF (*(parserPtr->spot + 1)))
|
||
++parserPtr->spot; // Move the pointer to the CRLF
|
||
break;
|
||
|
||
default:
|
||
// Control characters terminate the parse (which includes CRLF's)
|
||
if (iscntrl (*parserPtr->spot))
|
||
done = true;
|
||
break;
|
||
}
|
||
++parserPtr->spot;
|
||
}
|
||
|
||
// Copy the value -- which does NOT include the terminating delimiter (so we give it back)
|
||
if (!theError)
|
||
if (parserPtr->spot > oldSpot) {
|
||
--parserPtr->spot;
|
||
if (itemPtr && itemPtr->value)
|
||
theError = PtrPlusHand (oldSpot, itemPtr->value, (long) (parserPtr->spot - oldSpot));
|
||
}
|
||
else
|
||
if (!valueList)
|
||
parserPtr->error = vcErrorExpectingValue;
|
||
return (theError);
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParser8BitValue
|
||
//
|
||
// 8bit = <MIME RFC 1521 8-bit text>
|
||
//
|
||
// Pass nil for the 'itemPtr' if you don't care to actually save the value
|
||
//
|
||
|
||
static OSErr VCardParser8BitValue (VCardParserPtr parserPtr, VCardItemPtr itemPtr)
|
||
|
||
{
|
||
Ptr oldSpot;
|
||
OSErr theError;
|
||
|
||
theError = noErr;
|
||
|
||
oldSpot = parserPtr->spot;
|
||
|
||
// Skip past printable characters, terminating with a CR or LF
|
||
while (parserPtr->spot <= parserPtr->end && !isCR (*parserPtr->spot) && !isLF (*parserPtr->spot))
|
||
++parserPtr->spot;
|
||
|
||
// If the pointer moved, there is value to what we're doing
|
||
if (itemPtr)
|
||
if (parserPtr->spot > oldSpot)
|
||
theError = PtrPlusHand (oldSpot, itemPtr->value, (long) (parserPtr->spot - oldSpot));
|
||
else
|
||
parserPtr->error = vcErrorExpectingValue;
|
||
|
||
return (theError);
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParserQuotedPrintableValue
|
||
//
|
||
// quoted-printable = <MIME RFC 1521 quoted-printable text>
|
||
//
|
||
// Overly simplified algorithm for gathering the quoted printable text
|
||
// <09> Scan to the passed delimiter, or the end of the line if no delimiter is specified
|
||
// <09> If we hit and end of line and the last character is an '=' we've hit a soft return and keep scanning
|
||
//
|
||
|
||
static OSErr VCardParserQuotedPrintableValue (VCardParserPtr parserPtr, VCardItemPtr itemPtr, char delimiter)
|
||
|
||
{
|
||
return (VCardParser7BitValue (parserPtr, itemPtr, false, true));
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParserBase64Value
|
||
//
|
||
// base64 = <MIME RFC 1521 base64 text>
|
||
// ; the end of the text is marked with two CRLF sequences
|
||
//
|
||
// We're going to be real simpletons and just look for the CRLF CRLF
|
||
// sequence, passing back everything that precedes as the value. This
|
||
// is our best guess of legal Base64. The client should be better
|
||
// equipped to validate the value when it attempts to decode the value.
|
||
//
|
||
|
||
static OSErr VCardParserBase64Value (VCardParserPtr parserPtr, VCardItemPtr itemPtr)
|
||
|
||
{
|
||
Ptr oldSpot;
|
||
OSErr theError;
|
||
Boolean done;
|
||
|
||
theError = noErr;
|
||
|
||
oldSpot = parserPtr->spot;
|
||
|
||
// Scanning the data until we're 'done'.
|
||
done = false;
|
||
while (!done && parserPtr->spot <= parserPtr->end) {
|
||
if (isCR (*parserPtr->spot) || isLF (*parserPtr->spot))
|
||
done = (parserPtr->spot > oldSpot && (isCR (*(parserPtr->spot - 1)) || isLF (*(parserPtr->spot - 1))));
|
||
++parserPtr->spot;
|
||
}
|
||
|
||
// Copy the value -- which does NOT include either terminating CRLF (and we give one of them back)
|
||
if (parserPtr->spot - 1 > oldSpot) {
|
||
theError = PtrPlusHand (oldSpot, itemPtr->value, (long) (parserPtr->spot - oldSpot - 2));
|
||
--parserPtr->spot;
|
||
}
|
||
else
|
||
parserPtr->error = vcErrorExpectingValue;
|
||
|
||
return (theError);
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParserSkipToNextLine
|
||
//
|
||
// Skip past all characters on this line, leaving the 'spot' pointing to the first
|
||
// character on the next line.
|
||
//
|
||
|
||
static void VCardParserSkipToNextLine (VCardParserPtr parserPtr)
|
||
|
||
{
|
||
// Skip past all characters, skipping CR's and LF's
|
||
while (parserPtr->spot <= parserPtr->end && !isCR (*parserPtr->spot) && !isLF (*parserPtr->spot))
|
||
++parserPtr->spot;
|
||
VCardParseCRLFs (parserPtr, true);
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParserWS
|
||
//
|
||
// ws = 1*(SPACE / HTAB)
|
||
// ; "whitespace," one or more spaces or tabs
|
||
//
|
||
// Return 'true' if we actually did find white space, 'false' if not
|
||
//
|
||
|
||
static Boolean VCardParserWS (VCardParserPtr parserPtr, Boolean optional)
|
||
|
||
{
|
||
Ptr oldSpot;
|
||
Boolean wsPresent;
|
||
|
||
oldSpot = parserPtr->spot;
|
||
|
||
// Skip spaces and tabs
|
||
while (parserPtr->spot <= parserPtr->end && (isSPACE (*parserPtr->spot) || isHTAB (*parserPtr->spot)))
|
||
++parserPtr->spot;
|
||
|
||
if (wsPresent = (oldSpot == parserPtr->spot))
|
||
if (!optional)
|
||
parserPtr->error = vcErrorExpectingWhitespace;
|
||
return (wsPresent);
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// VCardParserWSLS
|
||
//
|
||
// wsls = 1*(SPACE / HTAB / CRLF)
|
||
// ; whitespace with line separators
|
||
//
|
||
// Return 'true' if we actually did find white space, 'false' if not
|
||
//
|
||
|
||
static Boolean VCardParserWSLS (VCardParserPtr parserPtr, Boolean optional)
|
||
|
||
{
|
||
Ptr oldSpot;
|
||
Boolean wsPresent;
|
||
|
||
oldSpot = parserPtr->spot;
|
||
|
||
// Skip spaces, tabs and CRLF's
|
||
while (parserPtr->spot <= parserPtr->end && (isSPACE (*parserPtr->spot) || isHTAB (*parserPtr->spot) || isCRLF (*parserPtr->spot)))
|
||
++parserPtr->spot;
|
||
|
||
if (wsPresent = (oldSpot == parserPtr->spot))
|
||
if (!optional)
|
||
parserPtr->error = vcErrorExpectingWhitespace;
|
||
return (wsPresent);
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParserStrnosemi
|
||
//
|
||
// strnosemi = *(*nosemi ("\;" / "\" CRLF)) *nosemi
|
||
//
|
||
// A string of 0 or more non-control ASCII characters, terminated by a semicolon.
|
||
//
|
||
// ';' or CRLF may appear in the string if escaped with a '\'
|
||
//
|
||
// At the conclusion of this function, 'spot' points to the first non-strnosemi character.
|
||
//
|
||
|
||
static OSErr VCardParserStrnosemi (VCardParserPtr parserPtr, VCardItemPtr itemPtr)
|
||
|
||
{
|
||
return (VCardParser7BitValue (parserPtr, itemPtr, true, itemPtr->encoding == vcValueQuotedPrintable));
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// VCardParserKeyword
|
||
//
|
||
// Parse the vCard data for a keyword. Defined here to be any string of one or more
|
||
// of the upper or lowercase characters A - Z, plus the digits 0 - 9. We'll also let
|
||
// in '-' characters so that we'll pickup things like X-EUDORA-SHOE-SIZE.
|
||
//
|
||
// The parsed string is placed in a Pascal style string, and the parser 'spot' is left
|
||
// pointing to the first character that follows the keyword.
|
||
//
|
||
|
||
static PStr VCardParserKeyword (VCardParserPtr parserPtr, PStr keyword)
|
||
|
||
{
|
||
*keyword = 0;
|
||
|
||
// copy alpha characters into the keyword
|
||
while (*keyword < sizeof (Str255) && parserPtr->spot <= parserPtr->end && (isalpha (*parserPtr->spot) || isdigit (*parserPtr->spot) || isDASH (*parserPtr->spot)))
|
||
keyword[++keyword[0]] = *parserPtr->spot++;
|
||
return (keyword);
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParserWord
|
||
//
|
||
// Parse the vCard data for a word. Defined here to be any string of one or more
|
||
// 7Bit us-ascii printable characted, excepting a couple of punctuation characters.
|
||
// The data is appended onto the end of the handle passed as a parameter.
|
||
//
|
||
// There is a potential bug here depending on how you choose to interpret the definition
|
||
// above. If a CR or a LF is found in the text -- but not a CRLF -- is it merely ignored
|
||
// with subsequent characters continueing to be scanned? Or does the first occurence of
|
||
// either terminate the scan? For now, we're doing the latter.
|
||
//
|
||
|
||
static OSErr VCardParserWord (VCardParserPtr parserPtr, Handle value)
|
||
|
||
{
|
||
Ptr oldSpot;
|
||
OSErr theError;
|
||
|
||
theError = noErr;
|
||
|
||
oldSpot = parserPtr->spot;
|
||
|
||
// Skip past printable characters, skipping CR's and LF's
|
||
while (parserPtr->spot <= parserPtr->end && isprint (*parserPtr->spot) && !strchr ("[]=:., ", *parserPtr->spot) && !isCR (*parserPtr->spot) && !isLF (*parserPtr->spot))
|
||
++parserPtr->spot;
|
||
|
||
// If the pointer moved, there is value to what we're doing
|
||
if (parserPtr->spot > oldSpot)
|
||
theError = PtrPlusHand (oldSpot, value, (long) (parserPtr->spot - oldSpot));
|
||
else
|
||
parserPtr->error = vcErrorExpectingValue;
|
||
|
||
return (theError);
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParserCharacter
|
||
//
|
||
// Parse a single specified character from the data stream, returning 'true' if the character
|
||
// was found, 'false' if not
|
||
//
|
||
|
||
static Boolean VCardParserCharacter (VCardParserPtr parserPtr, char c, Boolean optional)
|
||
|
||
{
|
||
Boolean found;
|
||
|
||
if (*parserPtr->spot == c) {
|
||
++parserPtr->spot;
|
||
found = true;
|
||
}
|
||
else {
|
||
if (!optional)
|
||
switch (c) {
|
||
case '.': parserPtr->error = vcErrorExpectingPeriod; break;
|
||
case ':': parserPtr->error = vcErrorExpectingColon; break;
|
||
case ';': parserPtr->error = vcErrorExpectingSemicolon; break;
|
||
default: parserPtr->error = vcErrorExpectingDelimiter; break;
|
||
}
|
||
found = false;
|
||
}
|
||
return (found);
|
||
}
|
||
|
||
|
||
//
|
||
// VCardParseCRLFs
|
||
//
|
||
// Parse carriage returns and linefeeds from the data stream.
|
||
//
|
||
// Return 'true' if we actually did find a CRLF, 'false' if not.
|
||
// At the conclusion of the function, 'spot' points to the first non-
|
||
// CRLF.
|
||
//
|
||
|
||
static Boolean VCardParseCRLFs (VCardParserPtr parserPtr, Boolean optional)
|
||
|
||
{
|
||
Ptr oldSpot;
|
||
Boolean crlfPresent;
|
||
|
||
oldSpot = parserPtr->spot;
|
||
|
||
// Skip CRLF's
|
||
while (parserPtr->spot <= parserPtr->end && isCRLF (*parserPtr->spot))
|
||
++parserPtr->spot;
|
||
|
||
if (crlfPresent = (oldSpot == parserPtr->spot))
|
||
if (!optional)
|
||
parserPtr->error = vcErrorExpectingCRLF;
|
||
|
||
return (crlfPresent);
|
||
}
|
||
|
||
|
||
static OSErr VCardAppendKeywordToGroup (VCardItemPtr itemPtr, Str255 keyword)
|
||
|
||
{
|
||
OSErr theError;
|
||
|
||
theError = noErr;
|
||
if (GetHandleSize (itemPtr->group))
|
||
theError = PtrPlusHand (".", itemPtr->group, 1);
|
||
if (!theError)
|
||
theError = PtrPlusHand (&keyword[1], itemPtr->group, *keyword);
|
||
return (theError);
|
||
}
|
||
|
||
|
||
static OSErr VCardParserAppendPropertyValueString (VCardParserPtr parserPtr, VCardParamPtr paramPtr, Handle strings)
|
||
|
||
{
|
||
OSErr theError;
|
||
|
||
paramPtr->pValue = vcKeyString;
|
||
paramPtr->offset = GetHandleSize (strings);
|
||
theError = VCardParserWord (parserPtr, strings);
|
||
if (!theError)
|
||
paramPtr->length = GetHandleSize (strings) - paramPtr->offset;
|
||
return (theError);
|
||
}
|
||
|
||
|
||
//
|
||
// VCardProcessProperty
|
||
//
|
||
// Check to see if a parsed keyword matches one of our known keywords, returning
|
||
// the ID for that keyword. If the keywword is unknown, return 'vcKeyNone'.
|
||
//
|
||
|
||
static vcKeywordType VCardProcessProperty (VCardParserPtr parserPtr, Str255 keyword)
|
||
|
||
{
|
||
vcKeywordType keywordID;
|
||
Str15 xDash;
|
||
short index;
|
||
|
||
keywordID = vcKeyNone;
|
||
// Does it match one of our known keywords?
|
||
if (index = FindSTRNIndex (VCardKeywordStrn, keyword))
|
||
keywordID = index + vcKeyBegin - 1;
|
||
else {
|
||
// Is it an "X-"?
|
||
if (BeginsWith (keyword, GetRString (xDash, VCardKeywordStrn + vcXDash)))
|
||
// One we know about, or one we don't
|
||
keywordID = (index = FindXDashString (parserPtr->xdash, keyword)) != -1 ? -(index + 1) : vcKeyOtherXDash;
|
||
else
|
||
parserPtr->error = vcErrorExpectingKeyword;
|
||
}
|
||
return (keywordID);
|
||
}
|
||
|
||
|
||
//
|
||
// FindXDashString
|
||
//
|
||
// Find an "X-" keyword in a list of "X-" strings, returning the index of the
|
||
// found string, or -1 if the keyword was not present.
|
||
//
|
||
|
||
static short FindXDashString (XDashStringHandle xdash, PStr keyword)
|
||
|
||
{
|
||
UPtr spot,
|
||
end;
|
||
short index;
|
||
Boolean found;
|
||
|
||
found = false;
|
||
index = 0;
|
||
if (xdash) {
|
||
spot = LDRef (xdash);
|
||
end = spot + GetHandleSize (xdash);
|
||
while (spot < end && !found) {
|
||
if (StringSame (spot, keyword))
|
||
found = true;
|
||
else {
|
||
++index;
|
||
spot += (*spot + 2);
|
||
}
|
||
}
|
||
UL (xdash);
|
||
}
|
||
return (found ? index : -1);
|
||
}
|
||
|
||
|
||
Boolean IsVCardFile (FSSpecPtr spec)
|
||
|
||
{
|
||
Str32 extension,
|
||
vcardExtension;
|
||
short i;
|
||
Boolean result;
|
||
|
||
if (!IsVCardAvailable ())
|
||
return (false);
|
||
|
||
result = false;
|
||
|
||
// Resolve if alias
|
||
IsAlias (spec, spec);
|
||
|
||
// Is this a Eudora vCard?
|
||
result = (FileTypeOf (spec) == VCARD_TYPE);
|
||
|
||
// How about an 8.3 vCard?
|
||
if (!result) {
|
||
for (i = spec->name[0]; i; --i)
|
||
if (spec->name[i] == '.')
|
||
break;
|
||
|
||
if (i) {
|
||
MakePStr (extension, &spec->name[i + 1], spec->name[0] - i);
|
||
|
||
result = striscmp (extension, GetRString (vcardExtension, VCARD_FILE_EXTENSION)) == 0;
|
||
}
|
||
}
|
||
|
||
// Hmmmm... maybe just a TEXT file (not created by Eudora)?
|
||
if (!result)
|
||
result = (FileTypeOf (spec) == 'TEXT' && FileCreatorOf (spec) != CREATOR);
|
||
|
||
// We could go an extra step here and peek into the file, checking for "BEGIN:VCARD"... nah.
|
||
if (result)
|
||
;
|
||
|
||
return (result);
|
||
}
|
||
|
||
|
||
//
|
||
// MakeVCardFileName
|
||
//
|
||
// Make an appropriate name for a vCard. For now, this is simply the nickname
|
||
// itself with ".vcf" appended.
|
||
//
|
||
|
||
PStr MakeVCardFileName (short ab,short nick, PStr filename)
|
||
|
||
{
|
||
Str31 extension;
|
||
|
||
GetNicknameNamePStr (ab, nick, filename);
|
||
filename[++filename[0]] = '.';
|
||
PCat (filename, GetRString (extension, VCARD_FILE_EXTENSION));
|
||
*filename = MIN (*filename, sizeof (Str31) - 1);
|
||
return (filename);
|
||
}
|
||
|
||
|
||
#endif
|