|
|
///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) Microsoft Corporation
//
// SYNOPSIS
//
// Defines the class EapProfile.
//
///////////////////////////////////////////////////////////////////////////////
#include "ias.h"
#include <algorithm>
#include "EapProfile.h"
#include <align.h>
EapProfile::EapProfile() throw () : begin(0), end(0), capacity(0) { }
EapProfile::~EapProfile() throw () { Clear(); CoTaskMemFree(begin); }
HRESULT EapProfile::Assign(const EapProfile& rhs) throw () { if (this == &rhs) { return S_OK; }
Clear(); Reserve(rhs.Size());
for (const ConfigData* i = rhs.begin; i != rhs.end; ++i) { end->length = i->length; end->value = static_cast<BYTE*>(CoTaskMemAlloc(i->length)); if (end->value == 0) { Clear(); return E_OUTOFMEMORY; } memcpy(end->value, i->value, i->length); ++end; }
return S_OK; }
HRESULT EapProfile::Load(VARIANT& src) throw () { HRESULT hr;
Clear();
if (V_VT(&src) == VT_EMPTY) { return S_OK; }
if (V_VT(&src) != (VT_ARRAY | VT_VARIANT)) { return DISP_E_TYPEMISMATCH; }
const SAFEARRAY* sa = V_ARRAY(&src); if (sa == 0) { return E_POINTER; }
// Find the first and last elements of the VARIANT array.
VARIANT* first = static_cast<VARIANT*>(sa->pvData); VARIANT* last = first + sa->rgsabound[0].cElements;
// Ensure that all the VARIANTs contain a valid config value.
for (const VARIANT* i = first; i != last; ++i) { hr = ValidateConfigChunk(*i); if (FAILED(hr)) { return hr; } }
// Sort the VARIANTs by type and sequence.
std::sort(first, last, LessThan);
// Gather the config for each type.
for (const VARIANT* j = first; j != last; ) { BYTE type = ExtractString(*j)[0];
// Find the end of the type's config.
const VARIANT* typeEnd = j + 1; while ((typeEnd != last) && (ExtractString(*typeEnd)[0] == type)) { ++typeEnd; }
// Gather the config for this type.
hr = GatherAndAppend(j, typeEnd); if (FAILED(hr)) { return hr; }
// Advance to the next type.
j = typeEnd; }
return S_OK; }
HRESULT EapProfile::Store(VARIANT& dst) throw () { HRESULT hr;
// Clear the out parameter.
VariantInit(&dst);
// Computer the number of VARIANTs required.
DWORD nelem = 0; for (const ConfigData* i = begin; i != end; ++i) { nelem += ChunksRequired(*i); }
// Allocate the SAFEARRAY for the result.
SAFEARRAY* sa = SafeArrayCreateVector(VT_VARIANT, 0, nelem); if (sa == 0) { return E_OUTOFMEMORY; }
// Scatter the config into the SAFEARRAY.
VARIANT* nextValue = static_cast<VARIANT*>(sa->pvData); for (const ConfigData* j = begin; j != end; ++j) { hr = Scatter(*j, nextValue); if (FAILED(hr)) { SafeArrayDestroy(sa); return hr; } }
// Store the result.
V_VT(&dst) = VT_ARRAY | VT_VARIANT; V_ARRAY(&dst) = sa;
return S_OK; }
void EapProfile::Clear() throw () { while (end > begin) { --end;
CoTaskMemFree(end->value); } }
void EapProfile::ClearExcept(BYTE type) throw () { for (ConfigData* i = begin; i != end; ) { if (i->value[0] != type) { // Free the config.
CoTaskMemFree(i->value);
// Decrement the number of elements.
--end;
// After Load completes we don't care if the array is sorted so we can
// just move the last element into the empty slot.
*i = *end; } else { ++i; } } }
void EapProfile::Erase(BYTE type) throw () { for (ConfigData* i = begin; i != end; ++i) { if (i->value[0] == type) { // Free the config.
CoTaskMemFree(i->value);
// Decrement the number of elements.
--end;
// After Load completes we don't care if the array is sorted so we can
// just move the last element into the empty slot.
*i = *end;
break; } } }
void EapProfile::Get(BYTE type, ConstConfigData& dst) const throw () { for (const ConfigData* i = begin; i != end; ++i) { if (i->value[0] == type) { // The EAP DLL doesn't want the type byte.
dst.length = i->length - ALIGN_WORST; dst.value = i->value + ALIGN_WORST; return; } }
dst.length = 0; dst.value = 0; }
HRESULT EapProfile::Set(BYTE type, const ConstConfigData& newConfig) throw () { if (newConfig.length == 0) { Erase(type); return S_OK; }
if (newConfig.value == 0) { return E_POINTER; }
if (ChunksRequired(newConfig) > maxChunks) { return E_INVALIDARG; }
// Ensure that we have room for the new element before we copy the value.
HRESULT hr = Reserve(Size() + 1); if (FAILED(hr)) { return hr; }
// One extra byte for the type tag. Rounded to ALIGN_WORST
DWORD len = newConfig.length + ALIGN_WORST; BYTE* val = static_cast<BYTE*>(CoTaskMemAlloc(len)); if (val == 0) { return E_OUTOFMEMORY; }
// Erase any existing config for the type.
Erase(type);
// Start with the lead type byte.
val[0] = type; // And then the rest of the configuration.
memcpy(val + ALIGN_WORST, newConfig.value, newConfig.length);
// Append the result.
end->length = len; end->value = val; ++end;
return S_OK; }
void EapProfile::Pop(ConfigData& dst) throw () { --end; dst = *end; }
void EapProfile::Swap(EapProfile& other) throw () { std::swap(begin, other.begin); std::swap(end, other.end); std::swap(capacity, other.capacity); }
inline EapProfile::SeqNum EapProfile::ExtractSequence(const BYTE* src) throw () { return (static_cast<SeqNum>(src[0]) << 8) | static_cast<SeqNum>(src[1]); }
inline void EapProfile::InsertSequence(SeqNum seq, BYTE* dst) throw () { dst[0] = static_cast<BYTE>((seq >> 8) & 0xFF); dst[1] = static_cast<BYTE>(seq & 0xFF); }
HRESULT EapProfile::GatherAndAppend( const VARIANT* first, const VARIANT* last ) throw () { HRESULT hr = Reserve(Size() + 1); if (FAILED(hr)) { return hr; }
// 1 type byte for the entire config.
DWORD len = ALIGN_WORST; for (const VARIANT* i = first; i != last; ++i) { // Ignore the SDO header.
len += ExtractLength(*i) - sdoHeaderSize; }
BYTE* val = static_cast<BYTE*>(CoTaskMemAlloc(len)); if (val == 0) { return E_OUTOFMEMORY; }
end->length = len; end->value = val; ++end;
// Get the type byte out of the first chunk.
val[0] = ExtractString(*first)[0]; // keep the real value aligned
val += ALIGN_WORST;
// Now concatenate the chunks, ignoring the header.
for (const VARIANT* j = first; j != last; ++j) { size_t chunkSize = (ExtractLength(*j) - sdoHeaderSize); memcpy(val, (ExtractString(*j) + sdoHeaderSize), chunkSize); val += chunkSize; }
return S_OK; }
HRESULT EapProfile::Scatter( const ConstConfigData& src, VARIANT*& dst ) throw () { BYTE type = src.value[0]; const BYTE* val = src.value + ALIGN_WORST; DWORD len = src.length - ALIGN_WORST;
// Sequence number.
SeqNum sequence = 0;
// Keep scattering until it's all gone.
while (len > 0) { // Compute the size of this chunk.
size_t chunkSize = (len > maxChunkSize) ? maxChunkSize : len;
// Create a SAFEARRAY of BYTEs to hold the data.
SAFEARRAY* sa = SafeArrayCreateVector( VT_UI1, 0, (chunkSize + sdoHeaderSize) ); if (sa == 0) { return E_OUTOFMEMORY; }
// Add the type byte and sequence number.
BYTE* chunk = static_cast<BYTE*>(sa->pvData); chunk[0] = type; InsertSequence(sequence, chunk + 1); memcpy(chunk + sdoHeaderSize, val, chunkSize);
// Store it in the dst VARIANT.
V_VT(dst) = VT_ARRAY | VT_UI1; V_ARRAY(dst) = sa; ++dst;
// Update our cursor.
val += chunkSize; len -= chunkSize; ++sequence; }
return S_OK; }
HRESULT EapProfile::Reserve(size_t newCapacity) throw () { if (newCapacity <= capacity) { return S_OK; }
// Ensure we grow wisely.
const size_t minGrowth = (capacity < 4) ? 4 : ((capacity * 3) / 2); if (newCapacity < minGrowth) { newCapacity = minGrowth; }
// Allocate the new array.
size_t nbyte = newCapacity * sizeof(ConfigData); ConfigData* newBegin = static_cast<ConfigData*>( CoTaskMemAlloc(nbyte) ); if (newBegin == 0) { return E_OUTOFMEMORY; }
// Save the existing data.
memcpy(newBegin, begin, Size() * sizeof(ConfigData));
// Update the state.
end = newBegin + Size(); capacity = newCapacity;
// Now it's safe to free the old array and swap in the new.
CoTaskMemFree(begin); begin = newBegin;
return S_OK; }
inline size_t EapProfile::ChunksRequired(const ConstConfigData& str) throw () { return ((str.length - ALIGN_WORST) + (maxChunkSize - 1)) / maxChunkSize; }
inline DWORD EapProfile::ExtractLength(const VARIANT& src) throw () { return V_ARRAY(&src)->rgsabound[0].cElements; }
inline const BYTE* EapProfile::ExtractString(const VARIANT& src) throw () { return static_cast<const BYTE*>(V_ARRAY(&src)->pvData); }
bool EapProfile::LessThan(const VARIANT& lhs, const VARIANT& rhs) throw () { const BYTE* val1 = ExtractString(lhs); const BYTE* val2 = ExtractString(rhs);
// Sort first by type, then sequence.
if (val1[0] < val2[0]) { return true; } else if (val1[0] == val2[0]) { return ExtractSequence(val1 + 1) < ExtractSequence(val2 + 1); } else { return false; } }
HRESULT EapProfile::ValidateConfigChunk(const VARIANT& value) throw () { if (V_VT(&value) != (VT_ARRAY | VT_UI1)) { return DISP_E_TYPEMISMATCH; }
const SAFEARRAY* sa = V_ARRAY(&value); if (sa == 0) { return E_POINTER; }
// The data has to be big enough for the header and 1 data byte.
if (sa->rgsabound[0].cElements <= sdoHeaderSize) { return E_INVALIDARG; }
return S_OK; }
|