/* File: IrIASService.cpp Contains: Implementation of IrDA's silly name server */ #include "IrIASService.h" #include "CListIterator.h" #include "CBuffer.h" #include "IrDALog.h" // consider splitting these into n-tables, one for each class? #if (hasTracing > 0 && hasIASServiceTracing > 0) enum tracecodes { kLogServiceNew = 1, kLogServiceFree, kLogClassNew, kLogClassFree, kLogAttrNew, kLogAttrFree, kLogNamedListFree, kLogElementNew, kLogElementFree }; static EventTraceCauseDesc TraceEvents[] = { {kLogServiceNew, "IrService: service new, obj="}, {kLogServiceFree, "IrService: service free, obj="}, {kLogClassNew, "IrService: class new, obj="}, {kLogClassFree, "IrService: class free, obj="}, {kLogAttrNew, "IrService: attr new, obj="}, {kLogAttrFree, "IrService: attr free, obj="}, {kLogNamedListFree, "IrService: named list free, obj="}, {kLogElementNew, "IrService: element new, obj="}, {kLogElementFree, "IrService: element free, obj="} }; #define XTRACE(x, y, z) IrDALogAdd( x, y, z, TraceEvents, true ) #else #define XTRACE(x, y, z) ((void)0) #endif #pragma mark //============== TIASService ============== //-------------------------------------------------------------------------------- // TIASService //-------------------------------------------------------------------------------- #define super TIASNamedList OSDefineMetaClassAndStructors(TIASService, TIASNamedList); /*static*/ TIASService * TIASService::tIASService(void) { TIASService *obj = new TIASService; XTRACE(kLogServiceNew, (int)obj >> 16, (short)obj); if (obj && !obj->Init()) { // this named list has no name obj->release(); obj = nil; } return obj; } //-------------------------------------------------------------------------------- // free //-------------------------------------------------------------------------------- void TIASService::free() { long index; XTRACE(kLogServiceFree, (int)this >> 16, (short)this); // Iterate thru the list of classes and delete each one. Note that I'm not // removing them because I'm depending on the CList destructor to do that for me. for (index = 0; index < this->GetArraySize(); index++) { TIASClass* theClass = (TIASClass*)this->At(index); theClass->release(); } super::free(); } // TIASService::free //-------------------------------------------------------------------------------- // AddIntegerEntry //-------------------------------------------------------------------------------- IrDAErr TIASService::AddIntegerEntry(const UChar* className, const UChar* attributeName, ULong intValue) { TIASElement* theEntry; // Create, initialize the new entry theEntry = TIASElement::tIASElement(intValue); // create and initialize require(theEntry, Fail_Element_New); // Let AddAttributeEntry finish the job (it will free theEntry if there are any errors) return AddAttributeEntry(className, attributeName, theEntry); Fail_Element_New: return kIrDAErrNoMemory; } // TIASService::AddIntegerEntry //-------------------------------------------------------------------------------- // AddStringEntry //-------------------------------------------------------------------------------- IrDAErr TIASService::AddStringEntry(const UChar* className, const UChar* attributeName, const UChar* stringValue, const UChar charSet, const ULong length) { TIASElement *theEntry; // Create, initialize the new entry theEntry = TIASElement::tIASElement(stringValue, charSet, length); // create and initialize require(theEntry, Fail_Element_New); // Let AddAttributeEntry finish the job (it will free theEntry if there are any errors) return AddAttributeEntry(className, attributeName, theEntry); Fail_Element_New: return kIrDAErrNoMemory; } // TIASService::AddStringEntry //-------------------------------------------------------------------------------- // AddNBytesEntry //-------------------------------------------------------------------------------- IrDAErr TIASService::AddNBytesEntry(const UChar* className, const UChar* attributeName, const UChar* aFewBytes, ULong length) { TIASElement* theEntry; // Create, initialize the new entry theEntry = TIASElement::tIASElement(aFewBytes, length); // create and initialize require(theEntry, Fail_Element_New); // Let AddAttributeEntry finish the job (it will free theEntry if there are any errors) return AddAttributeEntry(className, attributeName, theEntry); Fail_Element_New: return kIrDAErrNoMemory; } // TIASService::AddNBytesEntry //-------------------------------------------------------------------------------- // AddAttributeEntry //-------------------------------------------------------------------------------- IrDAErr TIASService::AddAttributeEntry(const UChar* className, const UChar* attributeName, TIASElement* theEntry) { IrDAErr result = kIrDAErrNoMemory; ULong flags; TIASAttribute *theAttr; // Make sure the class and attribute exist theAttr = AddAttribute(className, attributeName, flags); XREQUIRE(theAttr, Fail_ClassAttr_New); // Insert the new entry result = theAttr->Insert(theEntry); XREQUIRENOT(result, Fail_Entry_Insert); return noErr; Fail_Entry_Insert: RemoveAttribute(className, attributeName, flags); Fail_ClassAttr_New: theEntry->release(); return result; } // TIASService::AddAttributeEntry //-------------------------------------------------------------------------------- // AddAttribute //-------------------------------------------------------------------------------- TIASAttribute* TIASService::AddAttribute(const UChar* className, const UChar* attributeName, ULong& flags) { IrDAErr result; TIASClass* theClass; TIASAttribute* theAttr; theClass = AddClass(className, flags); XREQUIRE(theClass, Fail_Class_New); // Does the attribute already exist in this class theAttr = theClass->FindAttribute(attributeName); // If attribute exists, done if (theAttr != nil) { return theAttr; } // Create, initialize the new attribute theAttr = TIASAttribute::tIASAttribute(attributeName); require(theAttr, Fail_Attribute_New); result = theClass->Insert(theAttr); XREQUIRENOT(result, Fail_Attribute_Insert); // I created the attribute, let caller know so then can delete it if necessary flags |= kIASAddedAttribute; return theAttr; Fail_Attribute_Insert: theAttr->release(); Fail_Attribute_New: RemoveClass(className, flags); Fail_Class_New: return nil; } // TIASService::AddAttribute //-------------------------------------------------------------------------------- // AddClass //-------------------------------------------------------------------------------- TIASClass* TIASService::AddClass(const UChar* className, ULong& flags) { IrDAErr result; TIASClass *theClass; // Init flags flags = 0; // Does the class already exist in the name service theClass = FindClass(className); // If class exists, done if (theClass != nil) { return theClass; } // Create, initialize the new class theClass = TIASClass::tIASClass(className); require(theClass, Fail_Class_New); result = this->Insert(theClass); XREQUIRENOT(result, Fail_Class_Insert); // I created the class, let caller know so then can delete it if necessary flags |= kIASAddedClass; return theClass; Fail_Class_Insert: theClass->release(); Fail_Class_New: return nil; } // TIASService::AddClass //-------------------------------------------------------------------------------- // RemoveClass //-------------------------------------------------------------------------------- IrDAErr TIASService::RemoveClass(const UChar* className, ULong flags) { TIASClass *theClass; theClass = FindClass(className); if (theClass && (flags & kIASDeleteClass)) { this->Remove(theClass); theClass->release(); } return noErr; } // TIASService::RemoveClass //-------------------------------------------------------------------------------- // RemoveAttribute //-------------------------------------------------------------------------------- IrDAErr TIASService::RemoveAttribute(const UChar* className, const UChar* attributeName, ULong flags) { TIASClass* theClass; TIASAttribute* theAttr; theClass = FindClass(className); if (theClass) { theAttr = theClass->FindAttribute(attributeName); if (theAttr && (flags & kIASDeleteAttribute)) { theClass->Remove(theAttr); theAttr->release(); } if (flags & kIASDeleteClass) { this->Remove(theClass); theClass->release(); } } return noErr; } // TIASService::RemoveAttribute //-------------------------------------------------------------------------------- // FindClass //-------------------------------------------------------------------------------- TIASClass* TIASService::FindClass(const UChar* className) { return (TIASClass*)this->Search(className); } // TIASService::FindClass //-------------------------------------------------------------------------------- // FindAttribute //-------------------------------------------------------------------------------- TIASAttribute* TIASService::FindAttribute(const UChar* className, const UChar* attributeName) { TIASClass* theClass; TIASAttribute* theAttr = nil; theClass = FindClass(className); if (theClass) { theAttr = theClass->FindAttribute(attributeName); } return theAttr; } // TIASService::FindAttribute #pragma mark //============== TIASClass ================ #undef super #define super TIASNamedList OSDefineMetaClassAndStructors(TIASClass, TIASNamedList); //-------------------------------------------------------------------------------- // TIASClass //-------------------------------------------------------------------------------- /*static*/ TIASClass * TIASClass::tIASClass(const UChar *name) { TIASClass *obj = new TIASClass; XTRACE(kLogClassNew, (int)obj >> 16, (short)obj); if (obj && !obj->Init(name)) { obj->release(); obj = nil; } return obj; } //-------------------------------------------------------------------------------- // free //-------------------------------------------------------------------------------- void TIASClass::free() { long index; XTRACE(kLogClassFree, (int)this >> 16, (short)this); // Iterate thru the list of attributes and delete each one. Note that I'm not // removing them because I'm depending on the list destructor to do that for me. for (index = 0; index < this->GetArraySize(); index++) { TIASAttribute* theAttr = (TIASAttribute*)this->At(index); theAttr->release(); } super::free(); } // TIASClass::free //-------------------------------------------------------------------------------- // Insert //-------------------------------------------------------------------------------- IrDAErr TIASClass::Insert(TIASAttribute* attribute) { return CList::Insert((void*)attribute); } // TIASClass::Insert //-------------------------------------------------------------------------------- // FindAttribute //-------------------------------------------------------------------------------- TIASAttribute* TIASClass::FindAttribute(const UChar* attributeName) { return (TIASAttribute*)this->Search(attributeName); } // TIASClass::FindAttribute #pragma mark //============== TIASAttribute ============ #undef super #define super TIASNamedList OSDefineMetaClassAndStructors(TIASAttribute, TIASNamedList); //-------------------------------------------------------------------------------- // TIASAttribute //-------------------------------------------------------------------------------- /*static*/ TIASAttribute * TIASAttribute::tIASAttribute(const UChar *name) { TIASAttribute *obj = new TIASAttribute; XTRACE(kLogAttrNew, (int)obj >> 16, (short)obj); if (obj && !obj->Init(name)) { obj->release(); obj = nil; } return obj; } /*static*/ TIASAttribute * TIASAttribute::tIASAttribute(CBuffer *buffer) { TIASAttribute *obj = new TIASAttribute; XTRACE(kLogAttrNew, (int)obj >> 16, (short)obj); if (obj && !obj->InitFromBuffer(buffer)) { obj->release(); obj = nil; } return obj; } //-------------------------------------------------------------------------------- // free //-------------------------------------------------------------------------------- void TIASAttribute::free() { long index; XTRACE(kLogAttrFree, (int)this >> 16, (short)this); // Iterate thru the list of elements and delete each one. Note that I'm not // removing them because I'm depending on the list destructor to do that for me. for (index = 0; index < this->GetArraySize(); index++) { TIASElement* theElement = (TIASElement*)this->At(index); theElement->release(); } super::free(); } // TIASAttribute::free //-------------------------------------------------------------------------------- // Insert //-------------------------------------------------------------------------------- IrDAErr TIASAttribute::Insert(TIASElement* element) { return CList::Insert((void*)element); } // TIASAttribute::Insert //-------------------------------------------------------------------------------- // AddInfoToBuffer //-------------------------------------------------------------------------------- void TIASAttribute::AddInfoToBuffer(CBuffer* buffer) { long index; ULong arraySize = this->GetArraySize(); // Write out the number of elements (as a 16 byte quantity) buffer->Put((int)((arraySize >> 8) & 0xFF)); // Hi byte of short buffer->Put((int)((arraySize >> 0) & 0xFF)); // Lo byte of short // No iterate thru the elements and let each of them add themselves to the buffer for (index = 0; index < this->GetArraySize(); index++) { TIASElement* theElement = (TIASElement*)this->At(index); theElement->AddInfoToBuffer(buffer); } } // TIASAttribute::AddInfoToBuffer //-------------------------------------------------------------------------------- // InitFromBuffer //-------------------------------------------------------------------------------- Boolean TIASAttribute::InitFromBuffer(CBuffer* buffer) { ULong entryIndex; ULong listLength; UByte listLenBuf[2]; // Defined as a Big Endian UShort by protocol TIASElement* element; if (!super::Init()) return false; // All (successful) replies have a list length field if (buffer->Getn(listLenBuf, sizeof(listLenBuf)) != sizeof(listLenBuf)) { return false; } listLength = (ULong)(listLenBuf[0] * 256) + (ULong)listLenBuf[1]; // Get each attribute entry and add it to the attribute for (entryIndex = 0; entryIndex < listLength; entryIndex++) { IrDAErr result; // Create an attribute entry from the buffer info element = TIASElement::tIASElement(buffer); require(element, Fail); // Add the attr entry to the attribute (list) result = this->Insert(element); if (result != noErr) { element->release(); return false; } } return true; Fail: return false; } // TIASAttribute::ExtractInfoFromBuffer #pragma mark //============== TIASNamedList ============ #undef super #define super CList OSDefineMetaClassAndStructors(TIASNamedList, CList); // never created directly, no factories here! //-------------------------------------------------------------------------------- // free //-------------------------------------------------------------------------------- void TIASNamedList::free() { XTRACE(kLogNamedListFree, (int)this >> 16, (short)this); if (fName != nil) { IOFree(fName, fNameLen); fName = nil; } super::free(); } // TIASNamedList::free //-------------------------------------------------------------------------------- // Init //-------------------------------------------------------------------------------- Boolean TIASNamedList::Init(const UChar *theName) { fName = nil; if (!super::init()) return false; fNameLen = strlen((const char*)theName) + 1; if (fNameLen) { fName = (UChar*)IOMalloc(strlen((const char*)theName) + 1); require(fName, Fail); } strcpy((char*)fName, (const char*)theName); return true; Fail: return false; } // TIASNamedList::Init // // init w/out a name, seems kinda silly, but here we are // Boolean TIASNamedList::Init(void) { fName = nil; return super::init(); } //-------------------------------------------------------------------------------- // Search //-------------------------------------------------------------------------------- void* TIASNamedList::Search(const UChar* matchName) { CListIterator *iter = CListIterator::cListIterator(this); TIASNamedList* item; void* result = nil; int review_consider_putting_in_dynamic_cast; // to make sure list items are named lists for (item = (TIASNamedList*)iter->FirstItem(); iter->More(); item = (TIASNamedList*)iter->NextItem()) { if (strcmp((const char*)(item->fName), (const char*)matchName) == 0) { result = (void*)item; break; } } iter->release(); return result; } // TIASNamedList::Search #pragma mark //============== TIASElement ============== #undef super #define super OSObject OSDefineMetaClassAndStructors(TIASElement, OSObject); //-------------------------------------------------------------------------------- // TIASElement //-------------------------------------------------------------------------------- /*static*/ TIASElement * TIASElement::tIASElement(ULong theValue) { TIASElement *obj = new TIASElement; XTRACE(kLogElementNew, (int)obj >> 16, (short)obj); if (obj && !obj->init_with_long(theValue)) { obj->release(); obj = nil; } return obj; } /*static*/ TIASElement * TIASElement::tIASElement(const UChar* theBytes, ULong length) { TIASElement *obj = new TIASElement; XTRACE(kLogElementNew, (int)obj >> 16, (short)obj); if (obj && !obj->init_with_nbytes(theBytes, length)) { obj->release(); obj = nil; } return obj; } /*static*/ TIASElement * TIASElement::tIASElement(const UChar* theString, UChar charSet, ULong length) { TIASElement *obj = new TIASElement; XTRACE(kLogElementNew, (int)obj >> 16, (short)obj); if (obj && !obj->init_with_string(theString, charSet, length)) { obj->release(); obj = nil; } return obj; } /*static*/ TIASElement * TIASElement::tIASElement(CBuffer* buffer) { TIASElement *obj = new TIASElement; XTRACE(kLogElementNew, (int)obj >> 16, (short)obj); if (obj && !obj->init_with_buffer(buffer)) { obj->release(); obj = nil; } return obj; } //-------------------------------------------------------------------------------- // free //-------------------------------------------------------------------------------- void TIASElement::free() { int len; XTRACE(kLogElementFree, (int)this >> 16, (short)this); if (nameOrBytes && (nameOrBytes != (UByte*)&valueOrBytes)) { // if we allocated memory len = length; if (type == kIASValueString) // if a unicode string, then allocated memory is length+2 len += 2; IOFree(nameOrBytes, len); nameOrBytes = nil; } super::free(); } //// Inits Boolean TIASElement::init_with_long(ULong theValue) { type = kIASValueMissing; length = 0; valueOrBytes = 0; nameOrBytes = nil; characterSet = kIASCharSetAscii; if (!super::init()) return false; return SetInteger(theValue); } Boolean TIASElement::init_with_nbytes(const UChar* theBytes, ULong length) { type = kIASValueMissing; length = 0; valueOrBytes = 0; nameOrBytes = nil; characterSet = kIASCharSetAscii; if (!super::init()) return false; return SetNBytes(theBytes, length); } Boolean TIASElement::init_with_string(const UChar* theString, UChar charSet, ULong length) { type = kIASValueMissing; length = 0; valueOrBytes = 0; nameOrBytes = nil; characterSet = kIASCharSetAscii; if (!super::init()) return false; return SetString(theString, charSet, length); } Boolean TIASElement::init_with_buffer(CBuffer* buffer) { type = kIASValueMissing; length = 0; valueOrBytes = 0; nameOrBytes = nil; characterSet = kIASCharSetAscii; if (!super::init()) return false; return ExtractInfoFromBuffer(buffer); } //-------------------------------------------------------------------------------- // SetInteger //-------------------------------------------------------------------------------- Boolean TIASElement::SetInteger(ULong theValue) { type = kIASValueInteger; length = 4; valueOrBytes = theValue; nameOrBytes = (UByte*)&valueOrBytes; return true; } // TIASElement::SetInteger //-------------------------------------------------------------------------------- // SetNBytes //-------------------------------------------------------------------------------- Boolean TIASElement::SetNBytes(const UChar * theBytes, ULong theBytesLength) { type = kIASValueNBytes; length = theBytesLength; nameOrBytes = (UByte*)IOMalloc(length); require(nameOrBytes, Fail); BlockMove(theBytes, nameOrBytes, length); return true; Fail: return false; } // TIASElement::SetNBytes //-------------------------------------------------------------------------------- // SetString //-------------------------------------------------------------------------------- Boolean TIASElement::SetString(const UChar* theString, const UChar charSet, const ULong len) { type = kIASValueString; if (charSet != kIASCharSetUniCode) // if not unicode, use strlen to compute length length = (ULong)strlen((const char*)theString); else length = len; // if unicode, use supplied length valueOrBytes = 0; characterSet = charSet; // allocate room for string and two nulls (unicode or C) at end nameOrBytes = (UByte*)IOMalloc((unsigned int)(length+2)); require(nameOrBytes, Fail); //strcpy((char*)nameOrBytes, (const char*)theString); BlockMove(theString, nameOrBytes, length); // copy the string // would normally just have one null at the end of a C string, but // unicode "end of string" appears to want two nulls, since it's // a 16-bit encoding. So always append two nulls. nameOrBytes[length] = 0; nameOrBytes[length+1] = 0; return true; Fail: return false; } // TIASElement::SetString //-------------------------------------------------------------------------------- // GetInteger //-------------------------------------------------------------------------------- IrDAErr TIASElement::GetInteger(ULong *theValue) { if (type != kIASValueInteger) { return kIrDAErrGeneric; // ***FIXME: Better error return } *theValue = valueOrBytes; return noErr; } // TIASElement::GetInteger //-------------------------------------------------------------------------------- // GetNBytes //-------------------------------------------------------------------------------- IrDAErr TIASElement::GetNBytes(UByte **theBytes, ULong *theLength) { if (type != kIASValueNBytes) { return kIrDAErrGeneric; // ***FIXME: Better error return } *theBytes = nameOrBytes; // return pointer directly to our buffer *theLength = length; return noErr; } // TIASElement::GetNBytes //-------------------------------------------------------------------------------- // GetString //-------------------------------------------------------------------------------- IrDAErr TIASElement::GetString(UByte **theString, UByte *charSet, ULong *len) { if (type != kIASValueString) { return kIrDAErrGeneric; // ***FIXME: Better error return } *theString = nameOrBytes; // return pointer to our copy! if (charSet != nil) // if caller is asking for character set *charSet = characterSet; // return it too if (len != nil) // if caller supplied a length buffer *len = length; // return the length too (needed for unicode) return noErr; // hope the client doesn't clobber me :-) } // TIASElement::GetString //-------------------------------------------------------------------------------- // AddInfoToBuffer //-------------------------------------------------------------------------------- void TIASElement::AddInfoToBuffer(CBuffer* buffer) { UByte header[5]; UByte* pHdr = &header[0]; // Fill in the object id - not really exists, faking it if I can get away with it *pHdr++ = 0; *pHdr++ = 0; // Fill in the type *pHdr++ = type; XASSERT(length < 256); // Fill in ascii char set/lengths if (type == kIASValueNBytes) { *pHdr++ = 0; // Hi byte of length *pHdr++ = (UByte)length; // Lo byte of length } else if (type == kIASValueString) { *pHdr++ = characterSet; // Character set (defaults to ascii) *pHdr++ = (UByte)length; // Length of string } // Put out the header buffer->Putn(header, pHdr - header); // Put out the integer/string/octet sequence buffer->Putn(nameOrBytes, length); } // TIASElement::AddInfoToBuffer //-------------------------------------------------------------------------------- // ExtractInfoFromBuffer //-------------------------------------------------------------------------------- Boolean TIASElement::ExtractInfoFromBuffer(CBuffer* buffer) { UByte entryHeader[3]; // UShort object id followed by attr value type id UByte lengthInfo[2]; // high length byte, then low length byte UByte charSet; // character set ULong length; ULong intValue; Boolean rc; unsigned char buf[1024]; // max nBytes is 1024 (no null at end) // Each entry must have at least a 2-byte object id and an attr value type id byte if (buffer->Getn(entryHeader, sizeof(entryHeader)) != sizeof(entryHeader)) { return false; } // Determine how many bytes to input and "set" the appropriate type/value switch(entryHeader[2] /*type*/ ) { case kIASValueMissing: // This is the default value of an attr element - all done rc = true; break; case kIASValueInteger: if (buffer->Getn((UByte*)&intValue, sizeof(intValue)) != sizeof(intValue)) { return kIrDAErrGeneric; // FIXME: Return better error code } rc = SetInteger(intValue); break; case kIASValueNBytes: // first get the 16-bit length if (buffer->Getn(lengthInfo, sizeof(lengthInfo)) != sizeof(lengthInfo)) { return false; } length = lengthInfo[0] << 8 | lengthInfo[1]; // could read directly into a short check(length <= 1024); // according to spec check(length <= sizeof(buf)); // sanity if ((UInt32)buffer->Getn(buf, length) != length) return false; rc = SetNBytes(buf, length); break; case kIASValueString: // first get the character set code, length bytes if (buffer->Getn(lengthInfo, sizeof(lengthInfo)) != sizeof(lengthInfo)) { return kIrDAErrGeneric; // FIXME: Return better error code } charSet = lengthInfo[0]; // 1st byte is the character set length = lengthInfo[1]; // 2nd byte is the length if ((UInt32)buffer->Getn(buf, length) != length) return false; buf[length] = 0; // turn into C string before calling SetString // since length parm is ignored unless unicode charset rc = SetString(buf, charSet, length); // copy it into the attribute break; default: // Unknown type //DebugPrintf("extract info from buffer type %d", entryHeader[2]); // jdg rc = false; break; } return rc; } // TIASElement::ExtractInfoFromBuffer