mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1208 lines
34 KiB
1208 lines
34 KiB
///////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
|