///////////////////////////////////////////////////////////////////////////////////
//
// ACDTAPI.C
//
// This file handles all tapi functionality in the ACD sample
//
//
//
////////////////////////////////////////////////////////////////////////////////////
#include <windows.h>
#include <tapi.h>
#include "acdsmpl.h"
VOID CALLBACK LineCallback (DWORD hDevice,
DWORD dwMsg,
DWORD dwCallbackInstance,
DWORD dwParam1,
DWORD dwParam2,
DWORD dwParam3);
#define LogTapiError(__lResult__, __szString__)
#define LogError(__szString__)
extern ACDGLOBALS g;
////////////////////////////////////////////////////////////////////////////////////
//
// BOOL InitializeTapi()
//
// Whatever is needed to init TAPI for the application. This is called
// before the main window is created.
//
////////////////////////////////////////////////////////////////////////////////////
BOOL InitializeTapi()
{
DWORD dwAPIVersion;
LINEINITIALIZEEXPARAMS exparams;
LONG lResult;
DWORD i;
LPLINEDEVCAPS pLDC;
// fill in lineinitex parameters
exparams.dwTotalSize = sizeof(LINEINITIALIZEEXPARAMS);
exparams.dwOptions = LINEINITIALIZEEXOPTION_USEHIDDENWINDOW;
dwAPIVersion = TAPI_CURRENT_VERSION;
// line init
if ((lResult = lineInitializeEx(&g.hLineApp,
g.hInstance,
LineCallback,
SZAPPNAME,
&g.dwNumDevs,
&dwAPIVersion,
&exparams)) < 0)
{
LogTapiError(lResult, "lineInitializeEx");
return FALSE;
}
// if there are no tapi devices, should probably
// not continue
if (g.dwNumDevs == 0)
{
LogError("No TAPI devices installed");
lineShutdown(g.hLineApp);
return FALSE;
}
// need to get the permanent device IDs to map from
// an .ini file being read in
g.pdwPermIDs = (LPDWORD)ACDAlloc(g.dwNumDevs * sizeof(DWORD));
if (!g.pdwPermIDs)
{
return FALSE;
}
for (i = 0; i < g.dwNumDevs; i++)
{
pLDC = LineGetDevCaps(g.hLineApp,
i);
if (pLDC)
{
g.pdwPermIDs[i] = pLDC->dwPermanentLineID;
ACDFree(pLDC);
}
}
return TRUE;
}
//////////////////////////////////////////////////////////////////////
//
// BOOL CleanUp()
//
// Called while shutting down. free memory, close down tapi
//
//////////////////////////////////////////////////////////////////////
BOOL CleanUp()
{
PAGENT pAgent, pAgentNext;
PGROUP pGroup, pGroupNext;
// remove agents
pAgent = g.pAgents;
while(pAgent)
{
pAgentNext = pAgent->pNext;
DeleteAgent(pAgent);
pAgent = pAgentNext;
}
// remove groups
pGroup = g.pGroups;
while (pGroup)
{
pGroupNext = pGroup->pNext;
DeleteGroup(pGroup);
pGroup = pGroupNext;
}
// free id array
ACDFree(g.pdwPermIDs);
// shutdown
lineShutdown(g.hLineApp);
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////
//
// LRESULT MakeGroupList(PAGENT pAgent,
// LPLINEAGENTGROUPLIST pGroupList)
//
// Creates a LINEAGENTGROUPLIST for pAgent - group that the agent
// is allowed to log into
// Assumption: don't care about address for group list
//
////////////////////////////////////////////////////////////////////////////////
LRESULT MakeGroupList(PAGENT pAgent,
LPLINEAGENTGROUPLIST pGroupList)
{
PGROUP pGroup;
DWORD dwTotalSizeNeeded, dwNameOffset, dwNumEntries;
LPLINEAGENTGROUPENTRY pEntry;
LPTSTR pName;
pGroup = g.pGroups;
dwTotalSizeNeeded = sizeof(LINEAGENTGROUPLIST);
pGroupList->dwNumEntries = 0;
dwNumEntries = 0;
// walk list of groups
while (pGroup)
{
if (IsAgentInList(pGroup->pAgentList,
pAgent))
// if found the agent, add the group to the group list
{
// incrememt number of entries
dwNumEntries++;
// add to total size needed
dwTotalSizeNeeded += sizeof(LINEAGENTGROUPENTRY);
dwTotalSizeNeeded += (lstrlen(pGroup->lpszName) + 1) * sizeof(TCHAR);
}
pGroup = pGroup->pNext;
}
pGroupList->dwNeededSize = dwTotalSizeNeeded;
if (pGroupList->dwTotalSize < dwTotalSizeNeeded)
{
pGroupList->dwUsedSize = sizeof(LINEAGENTGROUPLIST);
return 0;
// return LINEERR_STRUCTURETOOSMALL;
}
pGroupList->dwNumEntries = dwNumEntries;
// set the list info
pGroupList->dwListSize = sizeof(LINEAGENTGROUPENTRY) * pGroupList->dwNumEntries;
pGroupList->dwListOffset = sizeof(LINEAGENTGROUPLIST);
// get the first agentgroup entry struct
pEntry = (LPLINEAGENTGROUPENTRY)(((LPBYTE)pGroupList) + pGroupList->dwListOffset);
dwNameOffset = pGroupList->dwListOffset + pGroupList->dwListSize;
pGroup = g.pGroups;
// loop through the groups again, and fill in the structure
while (pGroup)
{
if (IsAgentInList(pGroup->pAgentList,
pAgent))
{
// ID is just PGROUP
pEntry->GroupID.dwGroupID1 = (DWORD)pGroup;
pEntry->GroupID.dwGroupID2 = 0;
pEntry->GroupID.dwGroupID3 = 0;
pEntry->GroupID.dwGroupID4 = 0;
// set name of group
pName = (LPTSTR)(((LPBYTE)pGroupList) + dwNameOffset);
pEntry->dwNameSize = (lstrlen(pGroup->lpszName) + 1) * sizeof(TCHAR);
pEntry->dwNameOffset = dwNameOffset;
lstrcpy(pName,
pGroup->lpszName);
dwNameOffset += pEntry->dwNameSize;
// get next entry
pEntry++;
}
pGroup = pGroup->pNext;
}
pGroupList->dwUsedSize = dwTotalSizeNeeded;
return 0;
}
////////////////////////////////////////////////////////////////////////////
//
// LRESULT SetGroupList()
//
// Sets the groups that the agent is logged into.
// This does not change the groups that the agent _can_ log into
//
////////////////////////////////////////////////////////////////////////////
LRESULT SetGroupList(PAGENT pAgent,
DWORD dwAddress,
LPLINEAGENTGROUPLIST pGroupList)
{
LPLINEAGENTGROUPENTRY pGroupEntry;
PLISTITEM pListEntry;
DWORD i;
PGROUP * ppGroups = NULL;
PGROUP pGroup;
ppGroups = (PGROUP*)ACDAlloc(sizeof(PGROUP) * pGroupList->dwNumEntries);
// get to the group entry struct
pGroupEntry = (LPLINEAGENTGROUPENTRY)(((LPBYTE)pGroupList) + pGroupList->dwListOffset);
// loop through all entries
for (i = 0; i < pGroupList->dwNumEntries; i++)
{
// get the group in entry
// NOTE! NOTE! NOTE!
// should protect here against bad pointers !!!
pGroup = (PGROUP)pGroupEntry->GroupID.dwGroupID1;
if (pGroup->dwKey != GROUPKEY)
{
return LINEERR_INVALAGENTGROUP;
}
pListEntry = pGroup->pAgentList;
// walk list of agents in that group
if (!IsAgentInList(pGroup->pAgentList,
pAgent))
{
ACDFree(ppGroups);
return LINEERR_INVALAGENTGROUP;
}
// save group for easy access
ppGroups[i] = pGroup;
// get the next entry (after the variable portion of
// the previous entry struct)
pGroupEntry++;
}
// now we know that the groups to be set are valid
// walk through the list of groups again, and
// set the status to logged in/ not logged in
// for every group that the agent is a member of
pGroup = g.pGroups;
// walk list of all groups
while (pGroup)
{
if (pListEntry = IsAgentInList(pGroup->pAgentList,
pAgent))
{
// default to not logged in
pListEntry->bLoggedIn = FALSE;
// loop through groups being set
for (i = 0; i < pGroupList->dwNumEntries; i++)
{
// if this group is in list, set agent to logged in
if (pGroup == ppGroups[i])
{
pListEntry->bLoggedIn = TRUE;
// assumption: agent can only log into a group on one address.
pListEntry->dwAddress = dwAddress;
break;
}
} // for
} // agent in list
// next group
pGroup = pGroup->pNext;
} // while
ACDFree(ppGroups);
return 0;
}
/////////////////////////////////////////////////////////////////////////
//
// BOOL MakeAgentActivityList()
//
// Creates a LINEAGENTACTIVITYLIST for pAgent
//
// for the sample, just generic names are used
// "Activity 1", "Activity 2"....
//
/////////////////////////////////////////////////////////////////////////
LRESULT MakeAgentActivityList(PAGENT pAgent,
LPLINEAGENTACTIVITYLIST pActivityList)
{
TCHAR szBuffer[64];
DWORD dwTotalSize, dwNameOffset, i, dwNumEntries;
LPTSTR pName;
LPLINEAGENTACTIVITYENTRY pEntry;
// init
dwTotalSize = sizeof(LINEAGENTACTIVITYLIST);
pActivityList->dwNumEntries = 0;
dwNumEntries = 0;
// just a static list of activities
for (i = 0; i < TOTALACTIVITIES; i++)
{
dwNumEntries++;
// create a name
wsprintf(szBuffer, TEXT("Activity %lu"), i);
// determine size of this entry
dwTotalSize += sizeof(LINEAGENTACTIVITYENTRY);
dwTotalSize += (lstrlen(szBuffer) + 1) * sizeof(TCHAR);
}
pActivityList->dwNeededSize = dwTotalSize;
// verify size
if (pActivityList->dwTotalSize < dwTotalSize)
{
pActivityList->dwUsedSize = sizeof(LINEAGENTACTIVITYLIST);
return 0;
// return LINEERR_STRUCTURETOOSMALL;
}
pActivityList->dwNumEntries = dwNumEntries;
// set list stuff
pActivityList->dwListSize = sizeof(LINEAGENTACTIVITYENTRY) * pActivityList->dwNumEntries;
pActivityList->dwListOffset = sizeof(LINEAGENTACTIVITYLIST);
// get first activityentry
pEntry = (LPLINEAGENTACTIVITYENTRY)(((LPBYTE)pActivityList) + pActivityList->dwListOffset);
dwNameOffset = pActivityList->dwListOffset + pActivityList->dwListSize;
// loop through activities again
for (i = 0; i < TOTALACTIVITIES; i++)
{
// fill in members
pEntry->dwID = i;
// create a name
wsprintf(szBuffer, TEXT("Activity %lu"), i);
pName = (LPTSTR)(((LPBYTE)pActivityList) + dwNameOffset);
pEntry->dwNameSize = (lstrlen(szBuffer) + 1) * sizeof(TCHAR);
pEntry->dwNameOffset = dwNameOffset;
lstrcpy(pName,
szBuffer);
dwNameOffset += pEntry->dwNameSize;
pEntry++;
} // for
// fill in used size
pActivityList->dwUsedSize = dwTotalSize;
return 0;
}
#define DWAGENTFEATURES LINEAGENTFEATURE_SETAGENTGROUP | \
LINEAGENTFEATURE_SETAGENTSTATE | \
LINEAGENTFEATURE_SETAGENTACTIVITY | \
LINEAGENTFEATURE_GETAGENTACTIVITYLIST | \
LINEAGENTFEATURE_GETAGENTGROUP
#define DWSTATES LINEAGENTSTATE_LOGGEDOFF | \
LINEAGENTSTATE_NOTREADY | \
LINEAGENTSTATE_READY | \
LINEAGENTSTATE_BUSYACD | \
LINEAGENTSTATE_BUSYINCOMING | \
LINEAGENTSTATE_BUSYOUTBOUND | \
LINEAGENTSTATE_BUSYOTHER | \
LINEAGENTSTATE_WORKINGAFTERCALL | \
LINEAGENTSTATE_UNKNOWN | \
LINEAGENTSTATE_UNAVAIL
#define DWNEXTSTATES LINEAGENTSTATE_LOGGEDOFF | \
LINEAGENTSTATE_NOTREADY | \
LINEAGENTSTATE_READY | \
LINEAGENTSTATE_BUSYACD | \
LINEAGENTSTATE_BUSYINCOMING | \
LINEAGENTSTATE_BUSYOUTBOUND | \
LINEAGENTSTATE_BUSYOTHER | \
LINEAGENTSTATE_WORKINGAFTERCALL | \
LINEAGENTSTATE_UNKNOWN | \
LINEAGENTSTATE_UNAVAIL
#define DWSTATUSMESSAGES LINEAGENTSTATUS_GROUP | \
LINEAGENTSTATUS_STATE | \
LINEAGENTSTATUS_NEXTSTATE | \
LINEAGENTSTATUS_ACTIVITY | \
LINEAGENTSTATUS_ACTIVITYLIST | \
LINEAGENTSTATUS_GROUPLIST | \
LINEAGENTSTATUS_CAPSCHANGE | \
LINEAGENTSTATUS_VALIDSTATES | \
LINEAGENTSTATUS_VALIDNEXTSTATES
////////////////////////////////////////////////////////////////////
//
// BOOL IsValidState(DWORD dwState)
//
////////////////////////////////////////////////////////////////////
BOOL IsValidState(DWORD dwState)
{
if (!dwState)
{
return TRUE;
}
if ((dwState) & (dwState - 1))
{
// more than one bit set
return FALSE;
}
// make sure it's one of the valid states
return (dwState & DWSTATES);
}
////////////////////////////////////////////////////////////////////
//
// BOOL IsValidNextState(DWORD dwState)
//
////////////////////////////////////////////////////////////////////
BOOL IsValidNextState(DWORD dwState)
{
if (!dwState)
{
return TRUE;
}
if ((dwState) & (dwState - 1))
{
// more than one bit set
return FALSE;
}
// make sure it's one of the valid states
return (dwState & DWNEXTSTATES);
}
///////////////////////////////////////////////////////////////////////
//
// BOOL IsValidActivityID(DWORD dwActivityID)
//
///////////////////////////////////////////////////////////////////////
BOOL IsValidActivityID(DWORD dwActivityID)
{
return (dwActivityID <= TOTALACTIVITIES);
}
////////////////////////////////////////////////////////////////////////
//
// LRESULT MakeAgentCaps(PAGENT pAgent,
// LPLINEAGENTCAPS pAgentCaps)
//
// Creates a LINEAGENTCAPS for pAgent
// Features/states/messages are hardcoded
// for this example
//
////////////////////////////////////////////////////////////////////////
LRESULT MakeAgentCaps(PAGENT pAgent,
LPLINEAGENTCAPS pAgentCaps)
{
DWORD dwStringSize;
dwStringSize = (lstrlen(SZAPPNAME) + 1) * sizeof(TCHAR);
pAgentCaps->dwNeededSize = sizeof(LINEAGENTCAPS) + dwStringSize;
if (pAgentCaps->dwTotalSize < pAgentCaps->dwNeededSize)
{
pAgentCaps->dwUsedSize = sizeof(LINEAGENTCAPS);
return 0;
// return LINEERR_STRUCTURETOOSMALL;
}
pAgentCaps->dwAgentHandlerInfoSize = dwStringSize;
pAgentCaps->dwAgentHandlerInfoOffset = sizeof(LINEAGENTCAPS);
pAgentCaps->dwCapsVersion = TAPI_CURRENT_VERSION;
// these features are hardcoded here.
// a real implementation may set specific features
// per agent or line or address
pAgentCaps->dwFeatures = DWAGENTFEATURES;
pAgentCaps->dwStates = DWSTATES;
pAgentCaps->dwNextStates = DWNEXTSTATES;
pAgentCaps->dwMaxNumGroupEntries = NUMGROUPENTRIES;
pAgentCaps->dwAgentStatusMessages = DWSTATUSMESSAGES;
// no extensions
pAgentCaps->dwNumAgentExtensionIDs = 0;
pAgentCaps->dwAgentExtensionIDListSize = 0;
pAgentCaps->dwAgentExtensionIDListOffset = 0;
pAgentCaps->dwUsedSize = pAgentCaps->dwNeededSize;
return 0;
}
//////////////////////////////////////////////////////////////////////////
//
// LRESULT GetAgentStatus()
//
// Creates a LINEAGENTSTATUS for pAgent
//
//////////////////////////////////////////////////////////////////////////
LRESULT GetAgentStatus(PAGENT pAgent,
DWORD dwAddress,
LPLINEAGENTSTATUS pAgentStatus)
{
PGROUP pGroup;
LPLINEAGENTGROUPENTRY pGroupEntry;
DWORD dwTotalSize, dwNameOffset, dwCount;
TCHAR szActivityName[NAMESIZE];
PGROUP * ppGroups;
PLISTITEM pEntry;
// init total size
dwTotalSize = sizeof(LINEAGENTSTATUS);
if (dwAddress >= pAgent->dwNumAddresses)
{
return LINEERR_INVALADDRESSID;
}
// set know members
// for valid states / next states / agent features, just setting it to
// generic stuff. a real implementation may want to set these
// field depending on current state / agent / hline
pAgentStatus->dwState = pAgent->pAddressInfo[dwAddress].dwState;
pAgentStatus->dwNextState = pAgent->pAddressInfo[dwAddress].dwNextState;
pAgentStatus->dwActivityID = pAgent->pAddressInfo[dwAddress].dwActivity;
pAgentStatus->dwAgentFeatures = DWAGENTFEATURES;
pAgentStatus->dwValidStates = DWSTATES;
pAgentStatus->dwValidNextStates = DWNEXTSTATES;
// create the activity name
wsprintf(szActivityName, TEXT("Activity %lu"), pAgent->pAddressInfo[dwAddress].dwActivity);
dwTotalSize += (lstrlen(szActivityName) + 1) * sizeof(TCHAR);
ppGroups = (PGROUP *)ACDAlloc(sizeof(PGROUP) * g.dwNumGroups);
pGroup = g.pGroups;
pAgentStatus->dwNumEntries = 0;
// walk list of groups
while (pGroup)
{
pEntry = pGroup->pAgentList;
// walk each agent in each group
while (pEntry)
{
if (pEntry->pAgent == pAgent)
{
if ((!pEntry->bLoggedIn) ||
(pEntry->dwAddress != dwAddress))
{
break;
}
// save group
ppGroups[pAgentStatus->dwNumEntries] = pGroup;
// adjust total size / entries
pAgentStatus->dwNumEntries++;
dwTotalSize += sizeof(LINEAGENTGROUPENTRY);
dwTotalSize += (lstrlen(pGroup->lpszName) + 1) * sizeof(TCHAR);
break;
}
pEntry = pEntry->pNext;
} // while (pEntry)
pGroup = pGroup->pNext;
} // while (pGroup)
// set needed size
pAgentStatus->dwNeededSize = dwTotalSize;
// do we have enough room?
if (pAgentStatus->dwTotalSize < dwTotalSize)
{
// if not, return
pAgentStatus->dwUsedSize = sizeof(LINEAGENTSTATUS);
ACDFree(ppGroups);
return 0;
// return LINEERR_STRUCTURETOOSMALL;
}
// set the group entries...
// first get the offset to the first entry
pGroupEntry = (LPLINEAGENTGROUPENTRY)(((LPBYTE)pAgentStatus) + sizeof(LINEAGENTSTATUS));
pAgentStatus->dwGroupListOffset = sizeof(LINEAGENTSTATUS);
// figure out where the names can go (after all the fixed structures)
dwNameOffset = sizeof(LINEAGENTSTATUS) +
sizeof(LINEAGENTGROUPENTRY) * pAgentStatus->dwNumEntries;
// loop through all the group that the agent is logged into
for (dwCount = 0; dwCount < pAgentStatus->dwNumEntries; dwCount++)
{
// set the it (just the pGroup)
pGroupEntry->GroupID.dwGroupID1 = (DWORD)ppGroups[dwCount];
pGroupEntry->GroupID.dwGroupID2 = 0;
pGroupEntry->GroupID.dwGroupID3 = 0;
pGroupEntry->GroupID.dwGroupID4 = 0;
// set name size and offset
pGroupEntry->dwNameSize = (lstrlen(ppGroups[dwCount]->lpszName) + 1) * sizeof(TCHAR);
pGroupEntry->dwNameOffset = dwNameOffset;
// copy name
lstrcpy((LPTSTR)(((LPBYTE)pAgentStatus) + dwNameOffset),
ppGroups[dwCount]->lpszName);
// fix the name offset
dwNameOffset += pGroupEntry->dwNameSize;
// next entry
pGroupEntry++;
}
pAgentStatus->dwGroupListSize = dwNameOffset - pAgentStatus->dwGroupListOffset;
// put the activity name at the end
pAgentStatus->dwActivitySize = (lstrlen(szActivityName) + 1) * sizeof(TCHAR);
pAgentStatus->dwActivityOffset = dwNameOffset;
lstrcpy((LPTSTR)(((LPBYTE)pAgentStatus) + dwNameOffset),
szActivityName);
ACDFree(ppGroups);
pAgentStatus->dwUsedSize = pAgentStatus->dwNeededSize;
// return success
return 0;
}
/////////////////////////////////////////////////////////////////////
//
// LRESULT SetAgentState()
//
// Sets the current and next state for pAgent
// on that specific address
//
//
/////////////////////////////////////////////////////////////////////
LRESULT SetAgentState(PAGENT pAgent,
DWORD dwAddress,
DWORD dwState,
DWORD dwNextState)
{
// make sure valid
if ((!IsValidState(dwState)) ||
(!IsValidNextState(dwNextState)))
{
return LINEERR_INVALAGENTSTATE;
}
// check address
if (dwAddress >= pAgent->dwNumAddresses)
{
return LINEERR_INVALADDRESSID;
}
// set the state if specified
if (dwState)
{
pAgent->pAddressInfo[dwAddress].dwState = dwState;
}
if (dwNextState)
{
pAgent->pAddressInfo[dwAddress].dwNextState = dwNextState;
}
return 0;
}
///////////////////////////////////////////////////////////////////////////////
//
// LRESULT SetAgentActivity()
//
// Sets the activity for pAgent
//
///////////////////////////////////////////////////////////////////////////////
LRESULT SetAgentActivity(PAGENT pAgent,
DWORD dwAddress,
DWORD dwActivityID)
{
if (dwAddress >= pAgent->dwNumAddresses)
{
return LINEERR_INVALADDRESSID;
}
if (!IsValidActivityID(dwActivityID))
{
return LINEERR_INVALAGENTACTIVITY;
}
pAgent->pAddressInfo[dwAddress].dwActivity = dwActivityID;
return 0;
}
////////////////////////////////////////////////////////////////////////////////
//
// BOOL HandleOffering(HCALL hCall)
//
// Handles a LINECALLSTATE_OFFERING message
// Determines the group associated with the address
// that the call came in on, finds an available
// agent in that group, and transfers the call to that
// agent.
//
////////////////////////////////////////////////////////////////////////////////
BOOL HandleOffering(HCALL hCall)
{
LPLINECALLINFO pLCI;
PGROUP pGroup;
PLISTITEM pEntry;
LONG lResult;
pLCI = LineGetCallInfo(hCall);
if (!pLCI)
{
return FALSE;
}
pGroup = g.pGroups;
// find the group that this call came in on
// walk all the groups
while (pGroup)
{
// if the line and address match, it's the
// correct address
if ((pGroup->hLine == pLCI->hLine) &&
(pGroup->dwAddress == pLCI->dwAddressID))
{
break;
}
pGroup = pGroup->pNext;
}
// couldn't find the group
if (!pGroup)
{
// error!
ACDFree(pLCI);
return FALSE;
}
// OK - found the group that this call is for. Now transfer to
// an agent that is available.
pEntry = pGroup->pAgentList;
while (pEntry)
{
if (pEntry->bLoggedIn &&
(pEntry->pAgent->pAddressInfo[pEntry->dwAddress].dwState ==
LINEAGENTSTATE_READY))
{
// found someone
// doing a blind transfer here
// other implementations may need to
// do lineSetupTransfer / lineDial / lineCompleteTransfer
if (lResult = lineBlindTransfer(hCall,
(LPCWSTR)pEntry->pAgent->lpszNumber,
0))
{
//LogTapiError(TEXT("lineBlindTransfer"), lResult);
// don't break - try the next agent
}
else
{
// set the state to reflect that
// a call is being handled
SetAgentState(pEntry->pAgent,
pEntry->dwAddress,
LINEAGENTSTATE_BUSYACD,
LINEAGENTSTATE_READY);
break;
}
}
pEntry = pEntry->pNext;
}
if (!pEntry)
{
// couldn't find an available agent
// NOTE! NOTE! NOTE! NOTE! NOTE!
// something should be done here with this call. put into
// a queue on hold or something. For this sample, we are just
// ignoring it
}
ACDFree(pLCI);
return TRUE;
}
//////////////////////////////////////////////////////////////////////////
//
// BOOL HandleIdle(HCALL hCall)
//
// Handles LINECALLSTATE_IDLE
// Should always always always deallocate when
// getting an IDLE message. Also, determine if this is a call
// that we know about and set the agent state appropriatly
//
//////////////////////////////////////////////////////////////////////////
BOOL HandleIdle(HCALL hCall)
{
LPLINECALLINFO pLCI;
PAGENT pAgent;
pLCI = LineGetCallInfo(hCall);
// always deallocate the call
lineDeallocateCall(hCall);
if (!pLCI)
{
return FALSE;
}
// get the agent associated with the line
pAgent = GetAgentFromhLine(pLCI->hLine);
if (!pAgent)
{
ACDFree(pLCI);
return FALSE;
}
// set that agent to their next state
// Assumption: only calls that the ACD app know about
// occur. For example, if an agent made an outgoing call
// and it transitioned to idle, this code would still be executed.
// May make more sense to not handle NextState in the ACD app, and let
// the client app handle it.
SetAgentState(pAgent,
pLCI->dwAddressID,
pAgent->pAddressInfo[pLCI->dwAddressID].dwNextState,
0);
ACDFree(pLCI);
return TRUE;
}
////////////////////////////////////////////////////////////////////////////
//
// void HandleLineProxyRequest(HLINE hLine,
// LPLINEPROXYREQUEST pProxyRequest)
//
// Handles LINE_PROXYREQUEST message
// Just dispatches to appropriate functions
//
////////////////////////////////////////////////////////////////////////////
void HandleLineProxyRequest(HLINE hLine,
LPLINEPROXYREQUEST pProxyRequest)
{
PAGENT pAgent;
LRESULT lResult;
pAgent = GetAgentFromName((LPTSTR)(((LPBYTE)pProxyRequest) +
pProxyRequest->dwClientUserNameOffset));
if (!pAgent)
{
lineProxyResponse(hLine,
pProxyRequest,
LINEERR_INVALAGENTID);
return;
}
switch (pProxyRequest->dwRequestType)
{
case LINEPROXYREQUEST_SETAGENTGROUP:
lResult = SetGroupList(pAgent,
pProxyRequest->SetAgentGroup.dwAddressID,
&pProxyRequest->SetAgentGroup.GroupList);
lineProxyResponse(hLine,
pProxyRequest,
lResult);
return;
case LINEPROXYREQUEST_SETAGENTSTATE:
lResult = SetAgentState(pAgent,
pProxyRequest->SetAgentState.dwAddressID,
pProxyRequest->SetAgentState.dwAgentState,
pProxyRequest->SetAgentState.dwNextAgentState);
lineProxyResponse(hLine,
pProxyRequest,
lResult);
break;
case LINEPROXYREQUEST_SETAGENTACTIVITY:
lResult = SetAgentActivity(pAgent,
pProxyRequest->SetAgentActivity.dwAddressID,
pProxyRequest->SetAgentActivity.dwActivityID);
lineProxyResponse(hLine,
pProxyRequest,
lResult);
break;
case LINEPROXYREQUEST_GETAGENTSTATUS:
lResult = GetAgentStatus(pAgent,
pProxyRequest->GetAgentStatus.dwAddressID,
&pProxyRequest->GetAgentStatus.AgentStatus);
lineProxyResponse(hLine,
pProxyRequest,
lResult);
break;
case LINEPROXYREQUEST_GETAGENTCAPS:
if ((hLine == pAgent->hLine) &&
(pProxyRequest->GetAgentCaps.dwAddressID < pAgent->dwNumAddresses))
{
lResult = MakeAgentCaps(pAgent,
&pProxyRequest->GetAgentCaps.AgentCaps);
}
else
{
lResult = LINEERR_BADDEVICEID;
}
lineProxyResponse(hLine,
pProxyRequest,
lResult);
break;
case LINEPROXYREQUEST_GETAGENTACTIVITYLIST:
lResult = MakeAgentActivityList(pAgent,
&pProxyRequest->GetAgentActivityList.ActivityList);
lineProxyResponse(hLine,
pProxyRequest,
lResult);
break;
case LINEPROXYREQUEST_GETAGENTGROUPLIST:
lResult = MakeGroupList(pAgent,
&pProxyRequest->GetAgentGroupList.GroupList);
lineProxyResponse(hLine,
pProxyRequest,
lResult);
return;
}
return;
}
/////////////////////////////////////////////////////////////
//
// void HandleLineCallState(DWORD dwDevice,
//
// Handles callstate messages we are interested in
//
/////////////////////////////////////////////////////////////
void HandleLineCallState(DWORD dwDevice,
DWORD dwParam1,
DWORD dwParam2,
DWORD dwParam3)
{
switch (dwParam1)
{
case LINECALLSTATE_OFFERING:
{
LPLINECALLSTATUS pLCS;
// get the call privilege.
// NOTE: the new LINE_APPNEWCALL message notifies applications
// of their priv for new calls not created by app
pLCS = LineGetCallStatus((HCALL)dwDevice);
if (!pLCS)
{
break;
}
if (pLCS->dwCallPrivilege & LINECALLPRIVILEGE_OWNER)
{
HandleOffering((HCALL)dwDevice);
}
ACDFree(pLCS);
break;
}
case LINECALLSTATE_CONNECTED:
break;
case LINECALLSTATE_DISCONNECTED:
break;
case LINECALLSTATE_IDLE:
HandleIdle((HCALL)dwDevice);
break;
case LINECALLSTATE_BUSY:
break;
default:
break;
}
}
///////////////////////////////////////////////////////////////////////
// TAPI message handlers. For this sample, they don't
// do anything
///////////////////////////////////////////////////////////////////////
void HandleLineDevState(DWORD dwParam1,
DWORD dwParam2,
DWORD dwParam3)
{
}
void HandleLineReply(DWORD dwParam1,
DWORD dwParam2,
DWORD dwParam3)
{
}
void HandleLineCallInfo(DWORD dwParam1,
DWORD dwParam2,
DWORD dwParam3)
{
}
void HandleLineClose(DWORD dwParam1,
DWORD dwParam2,
DWORD dwParam3)
{
}
//////////////////////////////////////////////////////////////////////////////////
//
// LineCallback() - TAPI callback function
//
//////////////////////////////////////////////////////////////////////////////////
VOID CALLBACK LineCallback (DWORD hDevice,
DWORD dwMsg,
DWORD dwCallbackInstance,
DWORD dwParam1,
DWORD dwParam2,
DWORD dwParam3)
{
switch(dwMsg)
{
case LINE_PROXYREQUEST:
HandleLineProxyRequest((HLINE) hDevice,
(LPLINEPROXYREQUEST)dwParam1);
return;
case LINE_LINEDEVSTATE:
HandleLineDevState(dwParam1,
dwParam2,
dwParam3);
return;
case LINE_REPLY:
HandleLineReply(dwParam1,
dwParam2,
dwParam3);
return;
case LINE_CALLSTATE:
HandleLineCallState(hDevice,
dwParam1,
dwParam2,
dwParam3);
return;
case LINE_CALLINFO:
HandleLineCallInfo(dwParam1,
dwParam2,
dwParam3);
return;
case LINE_CLOSE:
HandleLineClose(dwParam1,
dwParam2,
dwParam3);
return;
default:
return;
}
}