home *** CD-ROM | disk | FTP | other *** search
/ kermit.columbia.edu / kermit.columbia.edu.tar / kermit.columbia.edu / panix / k95source.zip / ckntel.c < prev    next >
C/C++ Source or Header  |  2011-07-19  |  42KB  |  1,341 lines

  1. /* C K N T E L  --  Kermit Telephony interface for MS Win32 TAPI systems */
  2.  
  3. /*
  4.   Author: Jeffrey Altman (jaltman@secure-endpoints.com),
  5.           Secure Endpoints Inc., New York City.
  6.  
  7.   Copyright (C) 1985, 2004, Trustees of Columbia University in the City of New
  8.   York.
  9. */
  10.  
  11. /*
  12.  * =============================#includes=====================================
  13.  */
  14.  
  15. #include "ckcdeb.h"             /* Typedefs, debug formats, etc */
  16. #include "ckcker.h"             /* Kermit definitions */
  17. #include "ckntel.h"             /* Kermit Telephony */
  18. #include "ckucmd.h"
  19. #include <string.h>
  20.  
  21. #ifdef CK_TAPI
  22. #include <windows.h>
  23. #include <tapi.h>
  24. /* all functions in this module return TRUE to indicate success */
  25. /* or FALSE to indicate failure */
  26. #include "cknwin.h"
  27.  
  28. extern int cktapi ;
  29. extern struct keytab * tapilinetab, * tapilocationtab ;
  30. extern int ntapiline, ntapilocation ;
  31.  
  32. HINSTANCE hLib = NULL ;
  33. extern HINSTANCE hInstance ;
  34. extern char * termessage ;
  35.  
  36. int tapiopen = 0 ;
  37.  
  38. LONG (WINAPI *cklineInitialize)(LPHLINEAPP, HINSTANCE, LINECALLBACK, LPCSTR, LPDWORD ) = NULL ;
  39. LONG (WINAPI *cklineNegotiateAPIVersion)(HLINEAPP, DWORD, DWORD, DWORD, LPDWORD, LPLINEEXTENSIONID) = NULL ;
  40. LONG (WINAPI *cklineGetDevCaps)(HLINEAPP, DWORD, DWORD, DWORD, LPLINEDEVCAPS) = NULL ;
  41. LONG (WINAPI *cklineShutdown)(HLINEAPP) = NULL ;
  42. LONG (WINAPI *cklineOpen)(HLINEAPP, DWORD, LPHLINE, DWORD, DWORD, DWORD, DWORD, DWORD,
  43.                   LPLINECALLPARAMS) = NULL ;
  44. LONG (WINAPI *cklineMakeCall)(HLINE hLine, LPHCALL lphCall, LPCSTR lpszDestAddress,
  45.                       DWORD dwCountryCode, LPLINECALLPARAMS const lpCallParams) = NULL ;
  46. LONG (WINAPI *cklineDial)(HCALL hCall, LPCSTR lpszDestAddress, DWORD dwCountryCode) = NULL ;
  47. LONG (WINAPI *cklineDrop)(HCALL hCall, LPCSTR lpsUserUserInfo, DWORD dwSize) = NULL ;
  48. LONG (WINAPI *cklineClose)(HLINE hLine) = NULL ;
  49. LONG (WINAPI *cklineGetID)(HLINE hLine, DWORD dwAddressID, HCALL hCall,
  50.                    DWORD dwSelect, LPVARSTRING lpDeviceID, LPCSTR lpszDeviceClass) = NULL ;
  51. LONG (WINAPI *cklineGetTranslateCaps)( HLINEAPP hLineApp, DWORD,
  52.                                        LPLINETRANSLATECAPS lpLineTranslateCaps) = NULL ;
  53. LONG (WINAPI *cklineSetCurrentLocation)( HLINE hLine, DWORD dwLocationID ) = NULL ;
  54.  
  55. int cktapiunload(void)
  56. {
  57.    FreeLibrary( hLib ) ;
  58.    hLib = NULL ;
  59.    cklineInitialize = NULL ;
  60.    return TRUE ;
  61. }
  62.  
  63. int cktapiload(void)
  64. {
  65.    DWORD rc = 0 ;
  66.     hLib = LoadLibrary("tapi32") ;
  67.    if ( !hLib )
  68.    {
  69.       rc = GetLastError() ;
  70.       debug(F111, "cktapiload LoadLibrary failed","tapi32",rc) ;
  71.       return FALSE;
  72.    }
  73.  
  74.    if (((FARPROC) cklineInitialize = GetProcAddress( hLib, "lineInitialize" )) == NULL )
  75.    {
  76.       rc = GetLastError() ;
  77.       debug(F111, "cktapiload LoadLibrary failed","tapi32",rc) ;
  78.       return FALSE;
  79.    }
  80.    if (((FARPROC) cklineNegotiateAPIVersion = GetProcAddress( hLib, "lineNegotiateAPIVersion" )) == NULL )
  81.    {
  82.       rc = GetLastError() ;
  83.       debug(F111, "cktapiload LoadLibrary failed","tapi32",rc) ;
  84.       return FALSE;
  85.    }
  86.    if (((FARPROC) cklineGetDevCaps = GetProcAddress( hLib, "lineGetDevCaps" )) == NULL )
  87.    {
  88.       rc = GetLastError() ;
  89.       debug(F111, "cktapiload LoadLibrary failed","tapi32",rc) ;
  90.       return FALSE;
  91.    }
  92.    if (((FARPROC) cklineShutdown = GetProcAddress( hLib, "lineShutdown" )) == NULL )
  93.    {
  94.       rc = GetLastError() ;
  95.       debug(F111, "cktapiload LoadLibrary failed","tapi32",rc) ;
  96.       return FALSE;
  97.    }
  98.    if (((FARPROC) cklineOpen = GetProcAddress( hLib, "lineOpen" )) == NULL )
  99.    {
  100.       rc = GetLastError() ;
  101.       debug(F111, "cktapiload LoadLibrary failed","tapi32",rc) ;
  102.       return FALSE;
  103.    }
  104.    if (((FARPROC) cklineMakeCall = GetProcAddress( hLib, "lineMakeCall" )) == NULL )
  105.    {
  106.       rc = GetLastError() ;
  107.       debug(F111, "cktapiload LoadLibrary failed","tapi32",rc) ;
  108.       return FALSE;
  109.    }
  110.    if (((FARPROC) cklineDial = GetProcAddress( hLib, "lineDial" )) == NULL )
  111.    {
  112.       rc = GetLastError() ;
  113.       debug(F111, "cktapiload LoadLibrary failed","tapi32",rc) ;
  114.       return FALSE;
  115.    }
  116.    if (((FARPROC) cklineDrop = GetProcAddress( hLib, "lineDrop" )) == NULL )
  117.    {
  118.       rc = GetLastError() ;
  119.       debug(F111, "cktapiload LoadLibrary failed","tapi32",rc) ;
  120.       return FALSE;
  121.    }
  122.    if (((FARPROC) cklineClose = GetProcAddress( hLib, "lineClose" )) == NULL )
  123.    {
  124.       rc = GetLastError() ;
  125.       debug(F111, "cktapiload LoadLibrary failed","tapi32",rc) ;
  126.       return FALSE;
  127.    }
  128.    if (((FARPROC) cklineGetID = GetProcAddress( hLib, "lineGetID" )) == NULL )
  129.    {
  130.       rc = GetLastError() ;
  131.       debug(F111, "cktapiload LoadLibrary failed","tapi32",rc) ;
  132.       return FALSE;
  133.    }
  134.    if (((FARPROC) cklineGetTranslateCaps = GetProcAddress( hLib, "lineGetTranslateCaps" )) == NULL )
  135.    {
  136.       rc = GetLastError() ;
  137.       debug(F111, "cktapiload LoadLibrary failed","tapi32",rc) ;
  138.       return FALSE;
  139.    }
  140.    if (((FARPROC) cklineSetCurrentLocation = GetProcAddress( hLib, "lineSetCurrentLocation" )) == NULL )
  141.    {
  142.       rc = GetLastError() ;
  143.       debug(F111, "cktapiload LoadLibrary failed","tapi32",rc) ;
  144.       return FALSE;
  145.    }
  146.     return TRUE;
  147. }
  148.  
  149. // Global TAPI variables.
  150. HWND     g_hWndMainWindow = NULL;   // Apps main window.
  151. HWND     g_hDlgParentWindow = NULL; // This will be the parent of all dialogs.
  152. HLINEAPP g_hLineApp = NULL;
  153. DWORD    g_dwNumDevs = 0;
  154. DWORD    g_DataDevices = 0 ;
  155.  
  156. #define MAXDEVS 64
  157. LPLINEDEVCAPS g_lpLineDevCaps[64] ;
  158. extern HINSTANCE hInst ;
  159.  
  160. // Global variable that holds the handle to a TAPI dialog
  161. // that needs to be dismissed if line conditions change.
  162. HWND g_hDialog = NULL;
  163.  
  164. // Global flags to prevent re-entrancy problems.
  165. BOOL g_bShuttingDown = FALSE;
  166. BOOL g_bStoppingCall = FALSE;
  167. BOOL g_bInitializing = FALSE;
  168.  
  169.  
  170. // This sample only supports one call in progress at a time.
  171. BOOL g_bTapiInUse = FALSE;
  172.  
  173.  
  174. // Data needed per call.  This sample only supports one call.
  175. HCALL g_hCall = NULL;
  176. HLINE g_hLine = NULL;
  177. DWORD g_dwDeviceID = 0;
  178. DWORD g_dwAPIVersion = 0;
  179. DWORD g_dwCallState = 0;
  180. char  g_szDisplayableAddress[1024] = "";
  181. char  g_szDialableAddress[1024] = "";
  182. BOOL  g_bConnected = FALSE;
  183. LPVOID g_lpDeviceConfig = NULL;
  184. DWORD g_dwSizeDeviceConfig;
  185.  
  186. // Global variables to allow us to do various waits.
  187. BOOL  g_bReplyRecieved;
  188. DWORD g_dwRequestedID;
  189. long  g_lAsyncReply;
  190. BOOL  g_bCallStateReceived;
  191.  
  192. int cktapiinit(void)
  193. {
  194.    int i = 0 ;
  195.    g_hWndMainWindow = g_hDlgParentWindow = hwndGUI; // This will be the parent of all dialogs.
  196.  
  197.    for ( i ; i < MAXDEVS ; i++ )
  198.       g_lpLineDevCaps[i] = NULL ;
  199.  
  200.     if ( cktapiload() )
  201.     {
  202.         if (cktapiopen())
  203.         {
  204.            debug(F100,"TAPI open successful","",0);
  205.            g_DataDevices = cktapidevenum() ;
  206.            cktapiclose() ;
  207.            return TRUE ;
  208.         }
  209.         else
  210.         {
  211.             debug(F100,"TAPI open failed","",0);
  212.         }
  213.     }
  214.     return FALSE;
  215. }
  216.  
  217. void
  218. OutputDebugLastError( DWORD x, char * string )
  219. {
  220.     debug(F111,"TAPI LastError",string,x);
  221. }
  222.  
  223. void
  224. HandleNoMem( )
  225. {
  226.    debug(F100,"TAPI HandleNoMem","",0);
  227. }
  228.  
  229. //
  230. //  FUNCTION: LPVOID CheckAndReAllocBuffer(LPVOID, size_t, LPCSTR)
  231. //
  232. //  PURPOSE: Checks and ReAllocates a buffer if necessary.
  233. //
  234. //  PARAMETERS:
  235. //    lpBuffer          - Pointer to buffer to be checked.  Can be NULL.
  236. //    sizeBufferMinimum - Minimum size that lpBuffer should be.
  237. //    szApiPhrase       - Phrase to print if an error occurs.
  238. //
  239. //  RETURN VALUE:
  240. //    Returns a pointer to a valid buffer that is guarenteed to be
  241. //    at least sizeBufferMinimum size.
  242. //    Returns NULL if an error occured.
  243. //
  244. //  COMMENTS:
  245. //
  246. //    This function is a helper function intended to make all of the
  247. //    line API Wrapper Functions much simplier.  It allocates (or
  248. //    reallocates) a buffer of the requested size.
  249. //
  250. //    The returned pointer has been allocated with LocalAlloc,
  251. //    so LocalFree has to be called on it when you're finished with it,
  252. //    or there will be a memory leak.
  253. //
  254. //    Similarly, if a pointer is passed in, it *must* have been allocated
  255. //    with LocalAlloc and it could potentially be LocalFree()d.
  256. //
  257. //    If lpBuffer == NULL, then a new buffer is allocated.  It is
  258. //    normal to pass in NULL for this parameter the first time and only
  259. //    pass in a pointer if the buffer needs to be reallocated.
  260. //
  261. //    szApiPhrase is used only for debugging purposes.
  262. //
  263. //    It is assumed that the buffer returned from this function will be used
  264. //    to contain a variable sized structure.  Thus, the dwTotalSize field
  265. //    is always filled in before returning the pointer.
  266. //
  267. //
  268.  
  269. LPVOID CheckAndReAllocBuffer(
  270.     LPVOID lpBuffer, size_t sizeBufferMinimum, LPCSTR szApiPhrase)
  271. {
  272.     size_t sizeBuffer;
  273.  
  274.     if (lpBuffer == NULL)  // Allocate the buffer if necessary.
  275.     {
  276.         sizeBuffer = sizeBufferMinimum;
  277.         lpBuffer = (LPVOID) LocalAlloc(LPTR, sizeBuffer);
  278.  
  279.         if (lpBuffer == NULL)
  280.         {
  281.             OutputDebugString(szApiPhrase);
  282.             OutputDebugLastError(GetLastError(),"LocalAlloc : ");
  283.             HandleNoMem();
  284.             return NULL;
  285.         }
  286.     }
  287.     else // If the structure already exists, make sure its good.
  288.     {
  289.         sizeBuffer = LocalSize((HLOCAL) lpBuffer);
  290.  
  291.         if (sizeBuffer == 0) // Bad pointer?
  292.         {
  293.             OutputDebugString(szApiPhrase);
  294.             OutputDebugLastError(GetLastError(),"LocalSize : ");
  295.             return NULL;
  296.         }
  297.  
  298.         // Was the buffer big enough for the structure?
  299.         if (sizeBuffer < sizeBufferMinimum)
  300.         {
  301.             OutputDebugString(szApiPhrase);
  302.             OutputDebugString("Reallocating structure\n");
  303.             LocalFree(lpBuffer);
  304.             return CheckAndReAllocBuffer(NULL, sizeBufferMinimum, szApiPhrase);
  305.         }
  306.  
  307.         // Lets zero the buffer out.
  308.         memset(lpBuffer, 0, sizeBuffer);
  309.     }
  310.  
  311.     ((LPVARSTRING) lpBuffer ) -> dwTotalSize = (DWORD) sizeBuffer;
  312.     return lpBuffer;
  313. }
  314.  
  315.  
  316. void UpdateStatusBar( char * str, long param1, long param2 )
  317. {
  318.     printf( str ) ;
  319. }
  320.  
  321. void OutputDebugLineError( long param, char * str )
  322. {
  323.     debug(F111,"TAPI",str,param);
  324. }
  325.  
  326. void OutputDebugLineCallback(
  327.         DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance,
  328.         DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
  329. {
  330.     ;
  331. }
  332.  
  333. //
  334. //  FUNCTION: DoLineReply(..)
  335. //
  336. //  PURPOSE: Handle LINE_REPLY asynchronous messages.
  337. //
  338. //  PARAMETERS:
  339. //    dwDevice  - Line Handle associated with this LINE_REPLY.
  340. //    dwMsg     - Should always be LINE_REPLY.
  341. //    dwCallbackInstance - Unused by this sample.
  342. //    dwParam1  - Asynchronous request ID.
  343. //    dwParam2  - success or LINEERR error value.
  344. //    dwParam3  - Unused.
  345. //
  346. //  RETURN VALUE:
  347. //    none
  348. //
  349. //  COMMENTS:
  350. //
  351. //    All line API calls that return an asynchronous request ID
  352. //    will eventually cause a LINE_REPLY message.  Handle it.
  353. //
  354. //    This sample assumes only one call at time, and that we wait
  355. //    for a LINE_REPLY before making any other line API calls.
  356. //
  357. //    The only exception to the above is that we might shut down
  358. //    the line before receiving a LINE_REPLY.
  359. //
  360. //
  361.  
  362. void DoLineReply(
  363.     DWORD dwDevice, DWORD dwMessage, DWORD dwCallbackInstance,
  364.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
  365. {
  366.     if ((long) dwParam2 != SUCCESS)
  367.         OutputDebugLineError((long) dwParam2, "LINE_REPLY error: ");
  368.     else
  369.         OutputDebugString("LINE_REPLY: successfully replied.\n");
  370.  
  371.     // If we are currently waiting for this async Request ID
  372.     // then set the global variables to acknowledge it.
  373.     if (g_dwRequestedID == dwParam1)
  374.     {
  375.         g_bReplyRecieved = TRUE;
  376.         g_lAsyncReply = (long) dwParam2;
  377.     }
  378. }
  379.  
  380.  
  381. //
  382. //  FUNCTION: DoLineClose(..)
  383. //
  384. //  PURPOSE: Handle LINE_CLOSE asynchronous messages.
  385. //
  386. //  PARAMETERS:
  387. //    dwDevice  - Line Handle that was closed.
  388. //    dwMsg     - Should always be LINE_CLOSE.
  389. //    dwCallbackInstance - Unused by this sample.
  390. //    dwParam1  - Unused.
  391. //    dwParam2  - Unused.
  392. //    dwParam3  - Unused.
  393. //
  394. //  RETURN VALUE:
  395. //    none
  396. //
  397. //  COMMENTS:
  398. //
  399. //    This message is sent when something outside our app shuts
  400. //    down a line in use.
  401. //
  402. //    The hLine (and any hCall on this line) are no longer valid.
  403. //
  404. //
  405.  
  406. void DoLineClose(
  407.     DWORD dwDevice, DWORD dwMessage, DWORD dwCallbackInstance,
  408.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
  409. {
  410.     // Line has been shut down.  Clean up our internal variables.
  411.     g_hLine = NULL;
  412.     g_hCall = NULL;
  413.     ttclos(0);
  414.     termessage = "Call was shut down.";
  415. }
  416.  
  417.  
  418. //
  419. //  FUNCTION: DoLineCreate(..)
  420. //
  421. //  PURPOSE: Handle LINE_LINECREATE asynchronous messages.
  422. //
  423. //  PARAMETERS:
  424. //    dwDevice  - Unused.
  425. //    dwMsg     - Should always be LINE_CREATE.
  426. //    dwCallbackInstance - Unused.
  427. //    dwParam1  - dwDeviceID of new Line created.
  428. //    dwParam2  - Unused.
  429. //    dwParam3  - Unused.
  430. //
  431. //  RETURN VALUE:
  432. //    none
  433. //
  434. //  COMMENTS:
  435. //
  436. //    This message is new for Windows 95.  It is sent when a new line is
  437. //    added to the system.  This allows us to handle new lines without having
  438. //    to REINIT.  This allows for much more graceful Plug and Play.
  439. //
  440. //    This sample just changes the number of devices available and can use
  441. //    it next time a call is made.  It also tells the "Dial" dialog.
  442. //
  443. //
  444.  
  445. void DoLineCreate(
  446.     DWORD dwDevice, DWORD dwMessage, DWORD dwCallbackInstance,
  447.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
  448. {
  449.     // dwParam1 is the Device ID of the new line.
  450.     // Add one to get the number of total devices.
  451.     if (g_dwNumDevs <= dwParam1)
  452.         g_dwNumDevs = dwParam1+1;
  453.     g_DataDevices = cktapidevenum() ;
  454. }
  455.  
  456. //
  457. //  FUNCTION: DoLineDevState(..)
  458. //
  459. //  PURPOSE: Handle LINE_LINEDEVSTATE asynchronous messages.
  460. //
  461. //  PARAMETERS:
  462. //    dwDevice  - Line Handle that was closed.
  463. //    dwMsg     - Should always be LINE_LINEDEVSTATE.
  464. //    dwCallbackInstance - Unused by this sample.
  465. //    dwParam1  - LINEDEVSTATE constant.
  466. //    dwParam2  - Depends on dwParam1.
  467. //    dwParam3  - Depends on dwParam1.
  468. //
  469. //  RETURN VALUE:
  470. //    none
  471. //
  472. //  COMMENTS:
  473. //
  474. //    The LINE_LINEDEVSTATE message is received if the state of the line
  475. //    changes.  Examples are RINGING, MAINTENANCE, MSGWAITON.  Very few of
  476. //    these are relevant to this sample.
  477. //
  478. //    Assuming that any LINEDEVSTATE that removes the line from use by TAPI
  479. //    will also send a LINE_CLOSE message.
  480. //
  481. //
  482.  
  483. void DoLineDevState(
  484.     DWORD dwDevice, DWORD dwMessage, DWORD dwCallbackInstance,
  485.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
  486. {
  487.     switch(dwParam1)
  488.     {
  489.         case LINEDEVSTATE_RINGING:
  490.             UpdateStatusBar("Line Ringing",1,0);
  491.             OutputDebugString("Line Ringing.\n");
  492.             break;
  493.  
  494.         case LINEDEVSTATE_REINIT:
  495.         // This is an important case!  Usually means that a service provider
  496.         // has changed in such a way that requires TAPI to REINIT.
  497.         // Note that there are both 'soft' REINITs and 'hard' REINITs.
  498.         // Soft REINITs don't actually require a full shutdown but is instead
  499.         // just an informational change that historically required a REINIT
  500.         // to force the application to deal with.  TAPI API Version 1.3 apps
  501.         // will still need to do a full REINIT for both hard and soft REINITs.
  502.  
  503.             switch(dwParam2)
  504.             {
  505.                 // This is the hard REINIT.  No reason given, just REINIT.
  506.                 // TAPI is waiting for everyone to shutdown.
  507.                 // Our response is to immediately shutdown any calls,
  508.                 // shutdown our use of TAPI and notify the user.
  509.                 case 0:
  510.                     ttclos(0);
  511.                     cktapiclose();
  512.                     termessage = "Tapi line configuration has changed.";
  513.                     break;
  514.  
  515.                 case LINE_CREATE:
  516.                     OutputDebugString("Soft REINIT: LINE_CREATE.\n");
  517.                     DoLineCreate(dwDevice, dwParam2, dwCallbackInstance,
  518.                         dwParam3, 0, 0);
  519.                     break;
  520.  
  521.                 case LINE_LINEDEVSTATE:
  522.                     OutputDebugString("Soft REINIT: LINE_LINEDEVSTATE.\n");
  523.                     DoLineDevState(dwDevice, dwParam2, dwCallbackInstance,
  524.                         dwParam3, 0, 0);
  525.                     break;
  526.  
  527.                 // There might be other reasons to send a soft reinit.
  528.                 // No need to to shutdown for these.
  529.                 default:
  530.                     OutputDebugString("Ignoring soft REINIT\n");
  531.                     break;
  532.             }
  533.             break;
  534.  
  535.         case LINEDEVSTATE_OUTOFSERVICE:
  536.             termessage = "Line selected is now Out of Service.";
  537.             ttclos(0);
  538.             break;
  539.  
  540.         case LINEDEVSTATE_DISCONNECTED:
  541.             termessage = "Line selected is now disconnected.";
  542.             ttclos(0);
  543.             break;
  544.  
  545.         case LINEDEVSTATE_MAINTENANCE:
  546.             termessage = "Line selected is now out for maintenance.";
  547.             ttclos(0);
  548.             break;
  549.  
  550.     case LINEDEVSTATE_TRANSLATECHANGE:
  551.            #ifdef COMMENT
  552.             if (g_hDialog)
  553.                 PostMessage(g_hDialog, WM_COMMAND, IDC_CONFIGURATIONCHANGED, 0);
  554.            #endif
  555.             break;
  556.  
  557.         case LINEDEVSTATE_REMOVED:
  558.             OutputDebugString("A Line device has been removed;"
  559.                 " no action taken.\n");
  560.             break;
  561.  
  562.         default:
  563.             OutputDebugString("Unhandled LINEDEVSTATE message\n");
  564.     }
  565. }
  566.  
  567.  
  568.  
  569. //
  570. //  FUNCTION: DoLineCallState(..)
  571. //
  572. //  PURPOSE: Handle LINE_CALLSTATE asynchronous messages.
  573. //
  574. //  PARAMETERS:
  575. //    dwDevice  - Handle to Call who's state is changing.
  576. //    dwMsg     - Should always be LINE_CALLSTATE.
  577. //    dwCallbackInstance - Unused by this sample.
  578. //    dwParam1  - LINECALLSTATE constant specifying state change.
  579. //    dwParam2  - Specific to dwParam1.
  580. //    dwParam3  - LINECALLPRIVILEGE change, if any.
  581. //
  582. //  RETURN VALUE:
  583. //    none
  584. //
  585. //  COMMENTS:
  586. //
  587. //    This message is received whenever a call changes state.  Lots of
  588. //    things we do, ranging from notifying the user to closing the line
  589. //    to actually connecting to the target of our phone call.
  590. //
  591. //    What we do is usually obvious based on the call state change.
  592. //
  593.  
  594. void DoLineCallState(
  595.     DWORD dwDevice, DWORD dwMessage, DWORD dwCallbackInstance,
  596.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
  597. {
  598.     // Error if this CALLSTATE doesn't apply to our call in progress.
  599.     if ((HCALL) dwDevice != g_hCall)
  600.     {
  601.         debug(F101,"LINE_CALLSTATE: Unknown device ID '0x%lx'.","",dwDevice);
  602.         return;
  603.     }
  604.  
  605.     // This sets the global g_dwCallState variable so if we are waiting
  606.     // for a specific call state change, we will know when it happens.
  607.     g_dwCallState = dwParam1;
  608.     g_bCallStateReceived = TRUE;
  609.  
  610.     // dwParam3 contains changes to LINECALLPRIVILEGE, if there are any.
  611.     switch (dwParam3)
  612.     {
  613.         case 0:
  614.             break; // no change to call state
  615.  
  616.          // close line if we are made monitor.  Shouldn't happen!
  617.          case LINECALLPRIVILEGE_MONITOR:
  618.             OutputDebugString("line given monitor privilege; closing\n");
  619.             ttclos(0);
  620.             return;
  621.  
  622.          // close line if we are made owner.  Shouldn't happen!
  623.         case LINECALLPRIVILEGE_OWNER:
  624.             OutputDebugString("line given owner privilege; closing\n");
  625.             ttclos(0);
  626.             break;
  627.  
  628.         default: // Shouldn't happen!  All cases handled.
  629.             OutputDebugString("Unknown LINECALLPRIVILEGE message: closing\n");
  630.             ttclos(0);
  631.             return;
  632.     }
  633.  
  634.     // dwParam1 is the specific CALLSTATE change that is occurring.
  635.     switch (dwParam1)
  636.     {
  637.         case LINECALLSTATE_DIALTONE:
  638.             UpdateStatusBar("Dial Tone",1,0);
  639.             OutputDebugString("Dial Tone\n");
  640.             break;
  641.  
  642.         case LINECALLSTATE_DIALING:
  643.             UpdateStatusBar("Dialing Call",1,0);
  644.             OutputDebugString("Dialing\n");
  645.             break;
  646.  
  647.         case LINECALLSTATE_PROCEEDING:
  648.             UpdateStatusBar("Call is Proceeding",1,0);
  649.             OutputDebugString("Proceeding\n");
  650.             break;
  651.  
  652.         case LINECALLSTATE_RINGBACK:
  653.             UpdateStatusBar("RingBack",1,0);
  654.             OutputDebugString("RingBack\n");
  655.             break;
  656.  
  657.         case LINECALLSTATE_BUSY:
  658.             UpdateStatusBar("Line is busy",1,0);
  659.             OutputDebugString("Line busy, shutting down\n");
  660.             ttclos(0);
  661.             break;
  662.  
  663.         case LINECALLSTATE_IDLE:
  664.             UpdateStatusBar("Line is idle",1,0);
  665.             OutputDebugString("Line idle\n");
  666.             ttclos(0);
  667.             break;
  668.  
  669.         case LINECALLSTATE_SPECIALINFO:
  670.             UpdateStatusBar(
  671.                 "Special Info, probably couldn't dial number",1,0);
  672.             OutputDebugString(
  673.                 "Special Info, probably couldn't dial number\n");
  674.             ttclos(0);
  675.             break;
  676.  
  677.         case LINECALLSTATE_DISCONNECTED:
  678.         {
  679.             LPSTR pszReasonDisconnected;
  680.  
  681.             switch (dwParam2)
  682.             {
  683.                 case LINEDISCONNECTMODE_NORMAL:
  684.                     pszReasonDisconnected = "Remote Party Disconnected";
  685.                     break;
  686.  
  687.                 case LINEDISCONNECTMODE_UNKNOWN:
  688.                     pszReasonDisconnected = "Disconnected: Unknown reason";
  689.                     break;
  690.  
  691.                 case LINEDISCONNECTMODE_REJECT:
  692.                     pszReasonDisconnected = "Remote Party rejected call";
  693.                     break;
  694.  
  695.                 case LINEDISCONNECTMODE_PICKUP:
  696.                     pszReasonDisconnected =
  697.                         "Disconnected: Local phone picked up";
  698.                     break;
  699.  
  700.                 case LINEDISCONNECTMODE_FORWARDED:
  701.                     pszReasonDisconnected = "Disconnected: Forwarded";
  702.                     break;
  703.  
  704.                 case LINEDISCONNECTMODE_BUSY:
  705.                     pszReasonDisconnected = "Disconnected: Busy";
  706.                     break;
  707.  
  708.                 case LINEDISCONNECTMODE_NOANSWER:
  709.                     pszReasonDisconnected = "Disconnected: No Answer";
  710.                     break;
  711.  
  712.                 case LINEDISCONNECTMODE_BADADDRESS:
  713.                     pszReasonDisconnected = "Disconnected: Bad Address";
  714.                     break;
  715.  
  716.                 case LINEDISCONNECTMODE_UNREACHABLE:
  717.                     pszReasonDisconnected = "Disconnected: Unreachable";
  718.                     break;
  719.  
  720.                 case LINEDISCONNECTMODE_CONGESTION:
  721.                     pszReasonDisconnected = "Disconnected: Congestion";
  722.                     break;
  723.  
  724.                 case LINEDISCONNECTMODE_INCOMPATIBLE:
  725.                     pszReasonDisconnected = "Disconnected: Incompatible";
  726.                     break;
  727.  
  728.                 case LINEDISCONNECTMODE_UNAVAIL:
  729.                     pszReasonDisconnected = "Disconnected: Unavail";
  730.                     break;
  731.  
  732.                 case LINEDISCONNECTMODE_NODIALTONE:
  733.                     pszReasonDisconnected = "Disconnected: No Dial Tone";
  734.                     break;
  735.  
  736.                 default:
  737.                     pszReasonDisconnected =
  738.                         "Disconnected: LINECALLSTATE; Bad Reason";
  739.                     break;
  740.  
  741.             }
  742.  
  743.             UpdateStatusBar(pszReasonDisconnected,1,0);
  744.             OutputDebugString(pszReasonDisconnected);
  745.             OutputDebugString("\n");
  746.             ttclos(0);
  747.             break;
  748.         }
  749.  
  750.  
  751.         case LINECALLSTATE_CONNECTED:  // CONNECTED!!!
  752.         {
  753.             LPVARSTRING lpVarString = NULL;
  754.             DWORD dwSizeofVarString = sizeof(VARSTRING) + 1024;
  755.             HANDLE hCommFile = NULL;
  756.             long lReturn;
  757.  
  758.             // Very first, make sure this isn't a duplicated message.
  759.             // A CALLSTATE message can be sent whenever there is a
  760.             // change to the capabilities of a line, meaning that it is
  761.             // possible to receive multiple CONNECTED messages per call.
  762.             // The CONNECTED CALLSTATE message is the only one in TapiComm
  763.             // where it would cause problems if it where sent more
  764.             // than once.
  765.  
  766.             if (g_bConnected)
  767.                 break;
  768.  
  769.             g_bConnected = TRUE;
  770.  
  771.             // Get the handle to the comm port from the driver so we can start
  772.             // communicating.  This is returned in a LPVARSTRING structure.
  773.             do
  774.             {
  775.                 // Allocate the VARSTRING structure
  776.                 lpVarString = malloc( dwSizeofVarString ) ;
  777.  
  778.                 if (lpVarString == NULL)
  779.                     goto ErrorConnecting;
  780.  
  781.                 // Fill the VARSTRING structure
  782.                 lReturn = cklineGetID(0, 0, g_hCall, LINECALLSELECT_CALL,
  783.                     lpVarString, "comm/datamodem");
  784.  
  785.                 if (HandleLineErr(lReturn))
  786.                     ; // Still need to check if structure was big enough.
  787.                 else
  788.                 {
  789.                     OutputDebugLineError(lReturn,
  790.                         "lineGetID unhandled error: ");
  791.                     goto ErrorConnecting;
  792.                 }
  793.  
  794.                 // If the VARSTRING wasn't big enough, loop again.
  795.                 if ((lpVarString -> dwNeededSize) > (lpVarString -> dwTotalSize))
  796.                 {
  797.                     dwSizeofVarString = lpVarString -> dwNeededSize;
  798.                     lReturn = -1; // Lets loop again.
  799.                 }
  800.             }
  801.             while(lReturn != SUCCESS);
  802.  
  803.             OutputDebugString("Connected!  Starting communications!\n");
  804.  
  805.             // Again, the handle to the comm port is contained in a
  806.             // LPVARSTRING structure.  Thus, the handle is the very first
  807.             // thing after the end of the structure.  Note that the name of
  808.             // the comm port is right after the handle, but I don't want it.
  809.             hCommFile =
  810.                 *((LPHANDLE)((LPBYTE)lpVarString +
  811.                     lpVarString -> dwStringOffset));
  812.  
  813.  
  814.             // Started communications!
  815.            // if (StartComm(hCommFile))
  816.            // {
  817.            //     char szBuff[300];
  818.            //
  819.            //     wsprintf(szBuff,"Connected to '%s'",g_szDisplayableAddress);
  820.            //     UpdateStatusBar(szBuff, 1, 0);
  821.            //
  822.            //     free(lpVarString);
  823.            //     break;
  824.            // }
  825.  
  826.             // Couldn't start communications.  Clean up instead.
  827.           ErrorConnecting:
  828.  
  829.             // Its very important that we close all Win32 handles.
  830.             // The CommCode module is responsible for closing the hCommFile
  831.             // handle if it succeeds in starting communications.
  832.             if (hCommFile)
  833.                 CloseHandle(hCommFile);
  834.  
  835.             ttclos(0);
  836.             {
  837.                 char szBuff[300];
  838.                 wsprintf(szBuff,"Failed to Connect to '%s'",
  839.                     g_szDisplayableAddress);
  840.                 UpdateStatusBar(szBuff, 1, 0);
  841.             }
  842.  
  843.             if (lpVarString)
  844.                 free(lpVarString);
  845.  
  846.             break;
  847.         }
  848.  
  849.         default:
  850.             OutputDebugString("Unhandled LINECALLSTATE message\n");
  851.             break;
  852.     }
  853. }
  854.  
  855. void CALLBACK cklineCallbackFunc(
  856.     DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance,
  857.     DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
  858. {
  859.  
  860.     OutputDebugLineCallback(
  861.         dwDevice, dwMsg, dwCallbackInstance,
  862.         dwParam1, dwParam2, dwParam3);
  863.  
  864.     // All we do is dispatch the dwMsg to the correct handler.
  865.     switch(dwMsg)
  866.     {
  867.         case LINE_CALLSTATE:
  868.             DoLineCallState(dwDevice, dwMsg, dwCallbackInstance,
  869.                 dwParam1, dwParam2, dwParam3);
  870.             break;
  871.  
  872.         case LINE_CLOSE:
  873.             DoLineClose(dwDevice, dwMsg, dwCallbackInstance,
  874.                 dwParam1, dwParam2, dwParam3);
  875.             break;
  876.  
  877.         case LINE_LINEDEVSTATE:
  878.             DoLineDevState(dwDevice, dwMsg, dwCallbackInstance,
  879.                 dwParam1, dwParam2, dwParam3);
  880.             break;
  881.  
  882.         case LINE_REPLY:
  883.             DoLineReply(dwDevice, dwMsg, dwCallbackInstance,
  884.                 dwParam1, dwParam2, dwParam3);
  885.             break;
  886.  
  887.         case LINE_CREATE:
  888.             DoLineCreate(dwDevice, dwMsg, dwCallbackInstance,
  889.                 dwParam1, dwParam2, dwParam3);
  890.             break;
  891.  
  892.         default:
  893.             OutputDebugString("lineCallbackFunc message ignored\n");
  894.             break;
  895.  
  896.     }
  897.  
  898.     return;
  899.  
  900. }
  901.  
  902. BOOL HandleLineErr(long lLineErr)
  903. {
  904.    if ( lLineErr )
  905.     return FALSE;
  906.    else return TRUE ;
  907. }
  908.  
  909. BOOL HandleNoDevicesInstalled(void)
  910. {
  911.     return FALSE;
  912. }
  913.  
  914.  
  915. int cktapiopen(void)
  916. {
  917.     long lReturn;
  918.     BOOL bTryReInit = TRUE ;
  919.  
  920.    if ( tapiopen )
  921.    {
  922.       tapiopen++ ;
  923.       return TRUE ;
  924.    }
  925.  
  926.    // Initialize TAPI
  927.     do
  928.     {
  929.         lReturn = cklineInitialize(&g_hLineApp, hInstance,
  930.             cklineCallbackFunc, "Kermit", &g_dwNumDevs);
  931.  
  932.         // If we get this error, its because some other app has yet
  933.         // to respond to the REINIT message.  Wait 5 seconds and try
  934.         // again.  If it still doesn't respond, tell the user.
  935.         if (lReturn == LINEERR_REINIT)
  936.         {
  937.             if (bTryReInit)
  938.             {
  939.                 MSG msg;
  940.                 DWORD dwTimeStarted;
  941.  
  942.                 dwTimeStarted = GetTickCount();
  943.  
  944.                 while(GetTickCount() - dwTimeStarted < 5000)
  945.                 {
  946.                     if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
  947.                     {
  948.                         TranslateMessage(&msg);
  949.                         DispatchMessage(&msg);
  950.                     }
  951.                 }
  952.  
  953.                 bTryReInit = FALSE;
  954.                 continue;
  955.             }
  956.             else
  957.             {
  958.                 MessageBox(g_hDlgParentWindow,
  959.                     "A change to the system configuration requires that "
  960.                     "all Telephony applications relinquish their use of "
  961.                     "Telephony before any can progress.  "
  962.                     "Some have not yet done so."
  963.                     ,"Warning",MB_OK);
  964.                 g_bInitializing = FALSE;
  965.                 return FALSE;
  966.             }
  967.         }
  968.  
  969.         if (lReturn == LINEERR_NODEVICE)
  970.         {
  971.             if (HandleNoDevicesInstalled())
  972.                 continue;
  973.             else
  974.             {
  975.                 OutputDebugString("No devices installed.\n");
  976.                 g_bInitializing = FALSE;
  977.                 return FALSE;
  978.             }
  979.         }
  980.  
  981.         if (HandleLineErr(lReturn))
  982.             continue;
  983.         else
  984.         {
  985.             OutputDebugLineError(lReturn,
  986.                 "lineInitialize unhandled error: ");
  987.             g_bInitializing = FALSE;
  988.             return FALSE;
  989.         }
  990.     }
  991.    while(lReturn != SUCCESS);
  992.    return TRUE;
  993. }
  994.  
  995. int cktapidevenum( void )
  996. {
  997.    int i = 0 ;
  998.    DWORD dwAPIVersion ;
  999.    LINEEXTENSIONID ExtensionID ;
  1000.    int datalines = 0 ;
  1001.  
  1002.    /* Free existing LineDevCaps */
  1003.    for ( i = 0 ; i < MAXDEVS ; i++ )
  1004.    {
  1005.       if ( g_lpLineDevCaps[i] )
  1006.       {
  1007.          free( g_lpLineDevCaps[i] ) ;
  1008.          g_lpLineDevCaps[i] = NULL ;
  1009.       }
  1010.    }
  1011.  
  1012.    /* Enumerate current LineDevCaps */
  1013.    for ( i=0 ; i < g_dwNumDevs ; i++ )
  1014.    {
  1015.       g_lpLineDevCaps[i] = (LPLINEDEVCAPS) malloc (sizeof(LINEDEVCAPS)) ;
  1016.       g_lpLineDevCaps[i]->dwTotalSize = sizeof(LINEDEVCAPS) ;
  1017.       if ( cklineNegotiateAPIVersion(g_hLineApp, i,
  1018.                                     MAKELONG(4,1), MAKELONG(4,1),
  1019.                                     &dwAPIVersion, &ExtensionID))
  1020.       {
  1021.          free(g_lpLineDevCaps[i]);
  1022.          g_lpLineDevCaps[i] = NULL ;
  1023.          continue;
  1024.       }
  1025.       if (cklineGetDevCaps(g_hLineApp, i,
  1026.                           dwAPIVersion, 0, g_lpLineDevCaps[i]))
  1027.       {
  1028.          free(g_lpLineDevCaps[i]);
  1029.          g_lpLineDevCaps[i] = NULL ;
  1030.          continue;
  1031.       }
  1032.  
  1033.       if ( g_lpLineDevCaps[i]->dwNeededSize > g_lpLineDevCaps[i]->dwTotalSize )
  1034.       {
  1035.          DWORD NeededSize = g_lpLineDevCaps[i]->dwNeededSize;
  1036.          free(g_lpLineDevCaps[i]) ;
  1037.          g_lpLineDevCaps[i] = (LPLINEDEVCAPS) malloc (NeededSize) ;
  1038.          g_lpLineDevCaps[i]->dwTotalSize = NeededSize ;
  1039.  
  1040.          if (cklineGetDevCaps(g_hLineApp, i,
  1041.                                dwAPIVersion, 0, g_lpLineDevCaps[i]))
  1042.          {
  1043.             free(g_lpLineDevCaps[i]);
  1044.             g_lpLineDevCaps[i] = NULL ;
  1045.             continue;
  1046.          }
  1047.       }
  1048.  
  1049.       /* We now have a successful LineDevCaps structure */
  1050.       if ( g_lpLineDevCaps[i]->dwMediaModes & LINEMEDIAMODE_DATAMODEM )
  1051.       {
  1052.          /* then this is a valid line to use for data connections */
  1053.          datalines++ ;
  1054.       }
  1055.    }
  1056.    return datalines ;
  1057. }
  1058.  
  1059. int cktapiclose(void)
  1060. {
  1061.    if ( tapiopen > 0 )
  1062.       tapiopen-- ;
  1063.  
  1064.    if ( !tapiopen )
  1065.       cklineShutdown( g_hLineApp ) ;
  1066.    return TRUE;
  1067. }
  1068.  
  1069.  
  1070. int cktapidial(char * number)
  1071. {
  1072.  
  1073.     return TRUE;
  1074. }
  1075.  
  1076.  
  1077. int cktapiBuildLineTable( struct keytab ** pTable, int * pN )
  1078. {
  1079.    int i, n ;
  1080.    if ( *pTable )
  1081.    {
  1082.       for ( i=0 ; i < *pN ; i++ )
  1083.          free( (*pTable)[i].kwd ) ;
  1084.       free ( *pTable )  ;
  1085.    }
  1086.  
  1087.    *pTable = NULL ;
  1088.    *pN = 0 ;
  1089.  
  1090.    cktapiopen() ;
  1091.    n = cktapidevenum() ;
  1092.    cktapiclose() ;
  1093.  
  1094.    if ( n )
  1095.    {
  1096.       *pTable = malloc( sizeof(struct keytab) * n ) ;
  1097.  
  1098.       for ( i=0 ; i < g_dwNumDevs ; i++ )
  1099.          if ( g_lpLineDevCaps[i]->dwMediaModes & LINEMEDIAMODE_DATAMODEM )
  1100.          {
  1101.             (*pTable)[*pN].kwd = _strdup( ((char *)(g_lpLineDevCaps[i]))+g_lpLineDevCaps[i]->dwLineNameOffset) ;
  1102.             (*pTable)[*pN].kwval = i ;
  1103.             (*pTable)[*pN].flgs = 0 ;
  1104.             (*pN)++ ;
  1105.          }
  1106.    }
  1107. }
  1108.  
  1109. int cktapiBuildLocationTable( struct keytab ** pTable, int * pN )
  1110. {
  1111.    LPLINETRANSLATECAPS lpTranslateCaps = NULL;
  1112.    DWORD dwSizeofTranslateCaps = sizeof(LINETRANSLATECAPS);
  1113.    long lReturn = 0;
  1114.    DWORD dwCounter;
  1115.    LPLINELOCATIONENTRY lpLocationEntry;
  1116.    LPLINECARDENTRY lpLineCardEntry = NULL;
  1117.    DWORD dwPreferredCardID = MAXDWORD;
  1118.    int i = 0 ;
  1119.  
  1120.  
  1121.    if ( *pTable )
  1122.    {
  1123.       for ( i=0 ; i < *pN ; i++ )
  1124.          free( (*pTable)[i].kwd ) ;
  1125.       free ( *pTable )  ;
  1126.    }
  1127.    *pTable = NULL ;
  1128.    *pN = 0 ;
  1129.  
  1130.    cktapiopen() ;
  1131.  
  1132.    // First, get the TRANSLATECAPS
  1133.    do
  1134.    {
  1135.       lpTranslateCaps = (LPLINETRANSLATECAPS) CheckAndReAllocBuffer(
  1136.                  (LPVOID) lpTranslateCaps, dwSizeofTranslateCaps,
  1137.                  "cktapiFetchLocationInfo");
  1138.  
  1139.       if (lpTranslateCaps == NULL)
  1140.       {
  1141.          cktapiclose();
  1142.          return 0;
  1143.       }
  1144.  
  1145.       lReturn = (*cklineGetTranslateCaps)(g_hLineApp, 2,
  1146.                                       lpTranslateCaps);
  1147.  
  1148.       if (HandleLineErr(lReturn))
  1149.          ;
  1150.       else
  1151.       {
  1152.          OutputDebugLineError(lReturn,
  1153.                                "lineGetTranslateCaps unhandled error: ");
  1154.          LocalFree(lpTranslateCaps);
  1155.          cktapiclose();
  1156.          return 0;
  1157.       }
  1158.  
  1159.       if ((lpTranslateCaps -> dwNeededSize) >
  1160.            (lpTranslateCaps -> dwTotalSize))
  1161.       {
  1162.          dwSizeofTranslateCaps = lpTranslateCaps ->dwNeededSize;
  1163.          lReturn = -1; // Lets loop again.
  1164.       }
  1165.    }
  1166.    while(lReturn != SUCCESS);
  1167.  
  1168.    cktapiclose() ;
  1169.  
  1170.    // Find the location information in the TRANSLATECAPS
  1171.    lpLocationEntry = (LPLINELOCATIONENTRY)
  1172.       (((LPBYTE) lpTranslateCaps) + lpTranslateCaps->dwLocationListOffset);
  1173.  
  1174.    if ( lpTranslateCaps->dwNumLocations > 0 )
  1175.    {
  1176.       *pTable = malloc( sizeof(struct keytab) * lpTranslateCaps->dwNumLocations ) ;
  1177.  
  1178.       // enumerate all the locations
  1179.       for(dwCounter = 0;
  1180.            dwCounter < lpTranslateCaps -> dwNumLocations;
  1181.            dwCounter++)
  1182.       {
  1183.  
  1184.          // Put each one into the keytab
  1185.          (*pTable)[*pN].kwd = _strdup((((LPBYTE) lpTranslateCaps) +
  1186.                                                      lpLocationEntry[dwCounter].dwLocationNameOffset)) ;
  1187.          (*pTable)[*pN].kwval = i ;
  1188.          (*pTable)[*pN].flgs = 0 ;
  1189.          (*pN)++ ;
  1190.       }
  1191.    }
  1192.    LocalFree(lpTranslateCaps);
  1193. }
  1194.  
  1195. #ifdef COMMENT
  1196. int cktapiFetchLocationInfo( char * CurrentLocation,
  1197.                              char * CurrentCountry,
  1198.                              char * CurrentAreaCode )
  1199. {
  1200.    LPLINETRANSLATECAPS lpTranslateCaps = NULL;
  1201.    DWORD dwSizeofTranslateCaps = sizeof(LINETRANSLATECAPS);
  1202.    long lReturn;
  1203.    DWORD dwCounter;
  1204.    LPLINELOCATIONENTRY lpLocationEntry;
  1205.    LPLINECARDENTRY lpLineCardEntry = NULL;
  1206.    DWORD dwPreferredCardID = MAXDWORD;
  1207.  
  1208.    // First, get the TRANSLATECAPS
  1209.    do
  1210.    {
  1211.       lpTranslateCaps = (LPLINETRANSLATECAPS) CheckAndReAllocBuffer(
  1212.                  (LPVOID) lpTranslateCaps, dwSizeofTranslateCaps,
  1213.                  "cktapiFetchLocationInfo");
  1214.  
  1215.       if (lpTranslateCaps == NULL)
  1216.          return;
  1217.  
  1218.       lReturn = (*cklineGetTranslateCaps)(g_hLineApp, 2,
  1219.                                       lpTranslateCaps);
  1220.  
  1221.       if (HandleLineErr(lReturn))
  1222.          ;
  1223.       else
  1224.       {
  1225.          OutputDebugLineError(lReturn,
  1226.                                "lineGetTranslateCaps unhandled error: ");
  1227.          LocalFree(lpTranslateCaps);
  1228.          return;
  1229.       }
  1230.  
  1231.       if ((lpTranslateCaps -> dwNeededSize) >
  1232.            (lpTranslateCaps -> dwTotalSize))
  1233.       {
  1234.          dwSizeofTranslateCaps = lpTranslateCaps ->dwNeededSize;
  1235.          lReturn = -1; // Lets loop again.
  1236.       }
  1237.    }
  1238.    while(lReturn != SUCCESS);
  1239.  
  1240.    // Find the location information in the TRANSLATECAPS
  1241.    lpLocationEntry = (LPLINELOCATIONENTRY)
  1242.       (((LPBYTE) lpTranslateCaps) + lpTranslateCaps->dwLocationListOffset);
  1243.  
  1244.    // If lpszCurrentLocation, then make that location 'current'
  1245.    if (lpszCurrentLocation)
  1246.    {
  1247.       // loop through all locations, looking for a location match
  1248.       for(dwCounter = 0;
  1249.            dwCounter < lpTranslateCaps -> dwNumLocations;
  1250.            dwCounter++)
  1251.       {
  1252.          if (strcmp((((LPSTR) lpTranslateCaps) +
  1253.                       lpLocationEntry[dwCounter].dwLocationNameOffset),
  1254.                      lpszCurrentLocation)
  1255.               == 0)
  1256.          {
  1257.             // Found it!  Set the current location.
  1258.             (*cklineSetCurrentLocation)(g_hLineApp,
  1259.                                     lpLocationEntry[dwCounter].dwPermanentLocationID);
  1260.  
  1261.             // Set the return values.
  1262.             if (lpdwCountryID)
  1263.                *lpdwCountryID = lpLocationEntry[dwCounter].dwCountryID;
  1264.  
  1265.             if (lpszAreaCode)
  1266.                strcpy(lpszAreaCode, (((LPSTR) lpTranslateCaps) +
  1267.                                       lpLocationEntry[dwCounter].dwCityCodeOffset));
  1268.  
  1269.             // Store the preferred card ID for later use.
  1270.             dwPreferredCardID = lpLocationEntry[dwCounter].dwPreferredCardID;
  1271.             break;
  1272.          }
  1273.       }
  1274.  
  1275.       // Was a match for lpszCurrentLocation found?
  1276.       if (dwPreferredCardID == MAXDWORD)
  1277.       {
  1278.          OutputDebugString("lpszCurrentLocation not found\n");
  1279.          SendDlgItemMessage(hwndDlg, IDC_CALLINGCARD, WM_SETTEXT, 0,
  1280.                              (LPARAM) (LPCSTR) "Invalid Location Selected");
  1281.          LocalFree(lpTranslateCaps);
  1282.          return;
  1283.       }
  1284.    }
  1285.    else // fill the combobox and use the TAPI 'current' location.
  1286.    {
  1287.       // First empty the combobox
  1288.       SendDlgItemMessage(hwndDlg, IDC_LOCATION, CB_RESETCONTENT, 0, 0);
  1289.  
  1290.       // enumerate all the locations
  1291.       for(dwCounter = 0;
  1292.            dwCounter < lpTranslateCaps -> dwNumLocations;
  1293.            dwCounter++)
  1294.       {
  1295.          // Put each one into the combobox
  1296.          lReturn = SendDlgItemMessage(hwndDlg, IDC_LOCATION, CB_ADDSTRING,
  1297.                                        0, (LPARAM) (((LPBYTE) lpTranslateCaps) +
  1298.                                                      lpLocationEntry[dwCounter].dwLocationNameOffset));
  1299.  
  1300.          // Is this location the 'current' location?
  1301.          if (lpLocationEntry[dwCounter].dwPermanentLocationID ==
  1302.               lpTranslateCaps->dwCurrentLocationID)
  1303.          {
  1304.             // Return the requested information
  1305.             if (lpdwCountryID)
  1306.                *lpdwCountryID = lpLocationEntry[dwCounter].dwCountryID;
  1307.  
  1308.             if (lpszAreaCode)
  1309.                strcpy(lpszAreaCode, (((LPSTR) lpTranslateCaps) +
  1310.                                       lpLocationEntry[dwCounter].dwCityCodeOffset));
  1311.  
  1312.             // Set this to be the active location.
  1313.             SendDlgItemMessage(hwndDlg, IDC_LOCATION, CB_SETCURSEL, lReturn, 0);
  1314.             dwPreferredCardID = lpLocationEntry[dwCounter].dwPreferredCardID;
  1315.          }
  1316.       }
  1317.    }
  1318.  
  1319.    // Now locate the prefered card and display it.
  1320.  
  1321.    lpLineCardEntry = (LPLINECARDENTRY)
  1322.       (((LPBYTE) lpTranslateCaps) + lpTranslateCaps->dwCardListOffset);
  1323.  
  1324.    for(dwCounter = 0;
  1325.         dwCounter < lpTranslateCaps -> dwNumCards;
  1326.         dwCounter++)
  1327.     {
  1328.         if (lpLineCardEntry[dwCounter].dwPermanentCardID == dwPreferredCardID)
  1329.         {
  1330.             SendDlgItemMessage(hwndDlg, IDC_CALLINGCARD, WM_SETTEXT, 0,
  1331.                 (LPARAM) (((LPBYTE) lpTranslateCaps) +
  1332.                     lpLineCardEntry[dwCounter].dwCardNameOffset));
  1333.             break;
  1334.         }
  1335.     }
  1336.  
  1337.     LocalFree(lpTranslateCaps);
  1338. }
  1339. #endif /* COMMENT */
  1340. #endif /* CK_TAPI */
  1341.