home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 1999 October / PCpro_1999_10.ISO / Tools / vbcrypt / Control / Source / DESCTL.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1997-04-23  |  14.9 KB  |  570 lines

  1. // DESCtl.cpp : Implementation of the CDESCtrl ActiveX Control class.
  2.  
  3. #include "stdafx.h"
  4. #include "DES.h"
  5. #include "DESCtl.h"
  6. #include "DESPpg.h"
  7.  
  8.  
  9. #ifdef _DEBUG
  10. #define new DEBUG_NEW
  11. #undef THIS_FILE
  12. static char THIS_FILE[] = __FILE__;
  13. #endif
  14.  
  15.  
  16. IMPLEMENT_DYNCREATE(CDESCtrl, COleControl)
  17.  
  18.  
  19. /////////////////////////////////////////////////////////////////////////////
  20. // Message map
  21.  
  22. BEGIN_MESSAGE_MAP(CDESCtrl, COleControl)
  23.     //{{AFX_MSG_MAP(CDESCtrl)
  24.     // NOTE - ClassWizard will add and remove message map entries
  25.     //    DO NOT EDIT what you see in these blocks of generated code !
  26.     //}}AFX_MSG_MAP
  27.     ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)
  28. END_MESSAGE_MAP()
  29.  
  30.  
  31. /////////////////////////////////////////////////////////////////////////////
  32. // Dispatch map
  33.  
  34. BEGIN_DISPATCH_MAP(CDESCtrl, COleControl)
  35.     //{{AFX_DISPATCH_MAP(CDESCtrl)
  36.     DISP_PROPERTY_EX(CDESCtrl, "Key", GetKey, SetKey, VT_VARIANT)
  37.     DISP_FUNCTION(CDESCtrl, "EncryptFile", EncryptFile, VT_EMPTY, VTS_BSTR VTS_BSTR VTS_VARIANT)
  38.     DISP_FUNCTION(CDESCtrl, "EncryptString", EncryptString, VT_BSTR, VTS_VARIANT)
  39.     DISP_FUNCTION(CDESCtrl, "DecryptString", DecryptString, VT_BSTR, VTS_VARIANT)
  40.     DISP_FUNCTION(CDESCtrl, "DecryptFile", DecryptFile, VT_EMPTY, VTS_BSTR VTS_BSTR VTS_VARIANT)
  41.     //}}AFX_DISPATCH_MAP
  42.     DISP_FUNCTION_ID(CDESCtrl, "AboutBox", DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE)
  43. END_DISPATCH_MAP()
  44.  
  45.  
  46. /////////////////////////////////////////////////////////////////////////////
  47. // Event map
  48.  
  49. BEGIN_EVENT_MAP(CDESCtrl, COleControl)
  50.     //{{AFX_EVENT_MAP(CDESCtrl)
  51.     // NOTE - ClassWizard will add and remove event map entries
  52.     //    DO NOT EDIT what you see in these blocks of generated code !
  53.     //}}AFX_EVENT_MAP
  54. END_EVENT_MAP()
  55.  
  56.  
  57. /////////////////////////////////////////////////////////////////////////////
  58. // Property pages
  59.  
  60. // TODO: Add more property pages as needed.  Remember to increase the count!
  61. BEGIN_PROPPAGEIDS(CDESCtrl, 1)
  62.     PROPPAGEID(CDESPropPage::guid)
  63. END_PROPPAGEIDS(CDESCtrl)
  64.  
  65.  
  66. /////////////////////////////////////////////////////////////////////////////
  67. // Initialize class factory and guid
  68.  
  69. IMPLEMENT_OLECREATE_EX(CDESCtrl, "DES.DESCtrl.1",
  70.     0x7d7c6e7, 0xae76, 0x11d0, 0x9a, 0x6f, 0, 0x1, 0, 0, 0, 0)
  71.  
  72.  
  73. /////////////////////////////////////////////////////////////////////////////
  74. // Type library ID and version
  75.  
  76. IMPLEMENT_OLETYPELIB(CDESCtrl, _tlid, _wVerMajor, _wVerMinor)
  77.  
  78.  
  79. /////////////////////////////////////////////////////////////////////////////
  80. // Interface IDs
  81.  
  82. const IID BASED_CODE IID_DDES =
  83.         { 0x7d7c6e5, 0xae76, 0x11d0, { 0x9a, 0x6f, 0, 0x1, 0, 0, 0, 0 } };
  84. const IID BASED_CODE IID_DDESEvents =
  85.         { 0x7d7c6e6, 0xae76, 0x11d0, { 0x9a, 0x6f, 0, 0x1, 0, 0, 0, 0 } };
  86.  
  87.  
  88. /////////////////////////////////////////////////////////////////////////////
  89. // Control type information
  90.  
  91. static const DWORD BASED_CODE _dwDESOleMisc =
  92.     OLEMISC_INVISIBLEATRUNTIME |
  93.     OLEMISC_ACTIVATEWHENVISIBLE |
  94.     OLEMISC_SETCLIENTSITEFIRST |
  95.     OLEMISC_INSIDEOUT |
  96.     OLEMISC_CANTLINKINSIDE |
  97.     OLEMISC_RECOMPOSEONRESIZE;
  98.  
  99. IMPLEMENT_OLECTLTYPE(CDESCtrl, IDS_DES, _dwDESOleMisc)
  100.  
  101.  
  102. /////////////////////////////////////////////////////////////////////////////
  103. // CDESCtrl::CDESCtrlFactory::UpdateRegistry -
  104. // Adds or removes system registry entries for CDESCtrl
  105.  
  106. BOOL CDESCtrl::CDESCtrlFactory::UpdateRegistry(BOOL bRegister)
  107. {
  108.     // TODO: Verify that your control follows apartment-model threading rules.
  109.     // Refer to MFC TechNote 64 for more information.
  110.     // If your control does not conform to the apartment-model rules, then
  111.     // you must modify the code below, changing the 6th parameter from
  112.     // afxRegApartmentThreading to 0.
  113.  
  114.     if (bRegister)
  115.         return AfxOleRegisterControlClass(
  116.             AfxGetInstanceHandle(),
  117.             m_clsid,
  118.             m_lpszProgID,
  119.             IDS_DES,
  120.             IDB_DES,
  121.             afxRegApartmentThreading,
  122.             _dwDESOleMisc,
  123.             _tlid,
  124.             _wVerMajor,
  125.             _wVerMinor);
  126.     else
  127.         return AfxOleUnregisterClass(m_clsid, m_lpszProgID);
  128. }
  129.  
  130.  
  131. /////////////////////////////////////////////////////////////////////////////
  132. // Licensing strings
  133.  
  134. static const TCHAR BASED_CODE _szLicFileName[] = _T("DES.lic");
  135.  
  136. static const WCHAR BASED_CODE _szLicString[] =
  137.     L"Copyright (c) 1997 unbekannt";
  138.  
  139.  
  140. /////////////////////////////////////////////////////////////////////////////
  141. // CDESCtrl::CDESCtrlFactory::VerifyUserLicense -
  142. // Checks for existence of a user license
  143.  
  144. BOOL CDESCtrl::CDESCtrlFactory::VerifyUserLicense()
  145. {
  146.     return AfxVerifyLicFile(AfxGetInstanceHandle(), _szLicFileName,
  147.         _szLicString);
  148. }
  149.  
  150.  
  151. /////////////////////////////////////////////////////////////////////////////
  152. // CDESCtrl::CDESCtrlFactory::GetLicenseKey -
  153. // Returns a runtime licensing key
  154.  
  155. BOOL CDESCtrl::CDESCtrlFactory::GetLicenseKey(DWORD dwReserved,
  156.     BSTR FAR* pbstrKey)
  157. {
  158.     if (pbstrKey == NULL)
  159.         return FALSE;
  160.  
  161.     *pbstrKey = SysAllocString(_szLicString);
  162.     return (*pbstrKey != NULL);
  163. }
  164.  
  165.  
  166. /////////////////////////////////////////////////////////////////////////////
  167. // CDESCtrl::CDESCtrl - Constructor
  168.  
  169. CDESCtrl::CDESCtrl()
  170. {
  171.     InitializeIIDs(&IID_DDES, &IID_DDESEvents);
  172.  
  173.   m_Key = "";
  174.   bKeySet = FALSE;
  175.   SetInitialSize( 28,28 );
  176. }
  177.  
  178.  
  179. /////////////////////////////////////////////////////////////////////////////
  180. // CDESCtrl::~CDESCtrl - Destructor
  181.  
  182. CDESCtrl::~CDESCtrl()
  183. {
  184.     // TODO: Cleanup your control's instance data here.
  185. }
  186.  
  187.  
  188. /////////////////////////////////////////////////////////////////////////////
  189. // CDESCtrl::OnDraw - Drawing function
  190.  
  191. void CDESCtrl::OnDraw(
  192.             CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
  193. {
  194.       if( ( ( rcBounds.right - rcBounds.left ) <= 0 ) ||
  195.       ( ( rcBounds.bottom - rcBounds.top ) <= 0 ) )
  196.         return;
  197.  
  198.   CBitmap cbm;
  199.   CSize cs;
  200.   CBrush newBrush;
  201.   CPen newPen, *oldPen;
  202.   CRect r;
  203.  
  204.   if( cbm.LoadBitmap( IDB_CONTROLBITMAP ) )
  205.   {
  206.     r.left = rcBounds.left;
  207.     r.top = rcBounds.top;
  208.     r.right = rcBounds.left+28; // inklusive!
  209.     r.bottom = rcBounds.top+28;
  210.  
  211.     if( newBrush.CreateSolidBrush( RGB(0,0,0) ) )
  212.     {
  213.       pdc->FrameRect( &r, &newBrush );
  214.       r.InflateRect( -1,-1);
  215.       if( newPen.CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHILIGHT) ) )
  216.       {
  217.         oldPen = pdc->SelectObject( &newPen );
  218.         pdc->MoveTo( r.right-1,r.top);
  219.         pdc->LineTo( r.left,r.top);
  220.         pdc->LineTo( r.left,r.bottom);
  221.         pdc->SelectObject( oldPen );
  222.         newPen.DeleteObject();
  223.       }
  224.       if( newPen.CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW) ) )
  225.       {
  226.         oldPen = pdc->SelectObject( &newPen );
  227.         pdc->MoveTo( r.right-1,r.top);
  228.         pdc->LineTo( r.right-1,r.bottom-1 );
  229.         pdc->LineTo( r.left-1,r.bottom-1);
  230.         r.InflateRect( -1,-1);
  231.         pdc->MoveTo( r.right-1,r.top);
  232.         pdc->LineTo( r.right-1,r.bottom-1 );
  233.         pdc->LineTo( r.left-1,r.bottom-1);
  234.         r.right--;
  235.         r.bottom--;
  236.         pdc->SelectObject( oldPen );
  237.         newPen.DeleteObject();
  238.       }
  239.       CDC BackDC;
  240.       if( BackDC.CreateCompatibleDC( pdc ) )
  241.       {
  242.         CBitmap *oldBitmap = BackDC.SelectObject( &cbm );
  243.         pdc->BitBlt(r.left,r.top,23,23, &BackDC, 0,0, SRCCOPY );
  244.         BackDC.SelectObject( oldBitmap );
  245.  
  246.         BackDC.DeleteDC();
  247.       }
  248.       newBrush.DeleteObject();
  249.     }
  250.     cbm.DeleteObject();
  251.   }
  252. }
  253.  
  254.  
  255. /////////////////////////////////////////////////////////////////////////////
  256. // CDESCtrl::DoPropExchange - Persistence support
  257.  
  258. void CDESCtrl::DoPropExchange(CPropExchange* pPX)
  259. {
  260.     ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
  261.     COleControl::DoPropExchange(pPX);
  262.  
  263.     // TODO: Call PX_ functions for each persistent custom property.
  264.  
  265. }
  266.  
  267.  
  268. /////////////////////////////////////////////////////////////////////////////
  269. // CDESCtrl::OnResetState - Reset control to default state
  270.  
  271. void CDESCtrl::OnResetState()
  272. {
  273.     COleControl::OnResetState();  // Resets defaults found in DoPropExchange
  274.  
  275.     // TODO: Reset any other control state here.
  276. }
  277.  
  278.  
  279. /////////////////////////////////////////////////////////////////////////////
  280. // CDESCtrl::AboutBox - Display an "About" box to the user
  281.  
  282. void CDESCtrl::AboutBox()
  283. {
  284.     CDialog dlgAbout(IDD_ABOUTBOX_DES);
  285.     dlgAbout.DoModal();
  286. }
  287.  
  288.  
  289. /////////////////////////////////////////////////////////////////////////////
  290. // CDESCtrl message handlers
  291.  
  292. void CDESCtrl::DecryptFile(LPCTSTR CrypttextFile, LPCTSTR PlaintextFile, const VARIANT FAR& FailIfExists) 
  293. {
  294.   HANDLE hCrypt, hPlain;
  295.     LONG lFailIfExists = FALSE;
  296.   char InBuffer[512], OutBuffer[512];
  297.  
  298.   if( !bKeySet )
  299.     ThrowError(0);
  300.  
  301.   if( !IsError( &FailIfExists ) )
  302.     if( !GetBOOL( &FailIfExists, &lFailIfExists ) )
  303.         ThrowError(0);
  304.  
  305.   hCrypt = CreateFile(CrypttextFile, GENERIC_READ, FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL );
  306.   if( hCrypt == INVALID_HANDLE_VALUE )
  307.     ThrowError( CUSTOM_CTL_SCODE(0), "Kann Cryptfile nicht ÷ffnen");
  308.  
  309.   hPlain = CreateFile(PlaintextFile, GENERIC_WRITE, 0,NULL, lFailIfExists ? CREATE_NEW : CREATE_ALWAYS,0,NULL );
  310.   if( hPlain == INVALID_HANDLE_VALUE )
  311.   {
  312.     CloseHandle( hCrypt );
  313.     ThrowError( CUSTOM_CTL_SCODE(0), "Kann Plainfile nicht ÷ffnen");
  314.   }
  315.   while( TRUE )
  316.   {
  317.     DWORD dwRead;
  318.     DWORD dwWritten;
  319.     if( !ReadFile( hCrypt, InBuffer, sizeof( InBuffer ),&dwRead, NULL ) )
  320.     {
  321.       CloseHandle( hCrypt );
  322.       CloseHandle( hPlain ); 
  323.       ThrowError( CUSTOM_CTL_SCODE(0), "Fehler beim Lesen");
  324.     }
  325.     if( dwRead )
  326.     {
  327.       DWORD i = 0;
  328.       while( i + 8 < dwRead )
  329.       {
  330.         Decryptor.Codec( (DWORD*)&InBuffer[i], (DWORD*)&OutBuffer[i] );
  331.         i+=8;
  332.       }
  333.       // Den Rest des Lesebuffers mit Nullen fⁿllen
  334.  
  335.       memset( &InBuffer[dwRead], 0, sizeof( InBuffer ) - dwRead );
  336.  
  337.       dwRead += 7;
  338.       dwRead &= 0xFFFFFFF8;
  339.  
  340.       if( i < dwRead )
  341.         Decryptor.Codec( (DWORD*)&InBuffer[i], (DWORD*)&OutBuffer[i] );
  342.  
  343.  
  344.       if( !WriteFile( hPlain, OutBuffer, dwRead, &dwWritten, NULL ) )
  345.       {
  346.         CloseHandle( hCrypt );
  347.         CloseHandle( hPlain ); 
  348.         ThrowError( CUSTOM_CTL_SCODE(0), "Fehler beim Schreiben");
  349.       }
  350.     }
  351.     else
  352.       break;
  353.   }
  354.   CloseHandle( hCrypt );
  355.   CloseHandle( hPlain ); 
  356. }
  357.  
  358. void CDESCtrl::EncryptFile(LPCTSTR PlaintextFile, LPCTSTR CrypttextFile, const VARIANT FAR& FailIfExists) 
  359. {
  360.   HANDLE hCrypt, hPlain;
  361.     LONG lFailIfExists = FALSE;
  362.   char InBuffer[512], OutBuffer[512];
  363.  
  364.   if( !bKeySet )
  365.     ThrowError(0);
  366.  
  367.   if( !IsError( &FailIfExists ) )
  368.     if( !GetBOOL( &FailIfExists, &lFailIfExists ) )
  369.       ThrowError(0);
  370.   hPlain = CreateFile(PlaintextFile, GENERIC_READ, FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL );
  371.   if( hPlain == INVALID_HANDLE_VALUE )
  372.     ThrowError( CUSTOM_CTL_SCODE(0), "Kann Plaintextfile nicht ÷ffnen");
  373.  
  374.   hCrypt = CreateFile(CrypttextFile, GENERIC_WRITE, 0,NULL, lFailIfExists ? CREATE_NEW : CREATE_ALWAYS,0,NULL );
  375.   if( hCrypt == INVALID_HANDLE_VALUE )
  376.   {
  377.     CloseHandle( hPlain );
  378.     ThrowError( CUSTOM_CTL_SCODE(0), "Kann Cryptfile nicht ÷ffnen");
  379.   }
  380.   while( TRUE )
  381.   {
  382.     DWORD dwRead;
  383.     DWORD dwWritten;
  384.     if( !ReadFile( hPlain, InBuffer, sizeof( InBuffer ),&dwRead, NULL ) )
  385.     {
  386.       CloseHandle( hPlain ); 
  387.       CloseHandle( hCrypt );
  388.       ThrowError( CUSTOM_CTL_SCODE(0), "Fehler beim Lesen");
  389.     }
  390.     if( dwRead )
  391.     {
  392.       DWORD i = 0;
  393.       while( i + 8 < dwRead )
  394.       {
  395.         Encryptor.Codec( (DWORD*)&InBuffer[i], (DWORD*)&OutBuffer[i] );
  396.         i+=8;
  397.       }
  398.       // Den Rest des Lesebuffers mit Nullen fⁿllen
  399.  
  400.       memset( &InBuffer[dwRead], 0, sizeof( InBuffer ) - dwRead );
  401.  
  402.       dwRead += 7;
  403.       dwRead &= 0xFFFFFFF8;
  404.  
  405.       if( i < dwRead )
  406.         Encryptor.Codec( (DWORD*)&InBuffer[i], (DWORD*)&OutBuffer[i] );
  407.  
  408.  
  409.       if( !WriteFile( hCrypt, OutBuffer, dwRead, &dwWritten, NULL ) )
  410.       {
  411.         CloseHandle( hPlain ); 
  412.         CloseHandle( hCrypt );
  413.         ThrowError( CUSTOM_CTL_SCODE(0), "Fehler beim Schreiben");
  414.       }
  415.     }
  416.     else
  417.       break;
  418.   }
  419.   CloseHandle( hPlain ); 
  420.   CloseHandle( hCrypt );
  421.   
  422. }
  423.  
  424. BSTR CDESCtrl::EncryptString(const VARIANT FAR& Plaintext) 
  425. {
  426.     CString strResult;
  427.   CString ansi;
  428.   BSTR bstr;
  429.   LPSTR lpResult;
  430.   LPCTSTR lpAnsi;
  431.   DWORD dwOrg[2];
  432.   long l, i, lResult;
  433.  
  434.   if( !bKeySet )
  435.     ThrowError(0);
  436.  
  437.   if( !GetString( &Plaintext, &bstr ) )
  438.     ThrowError(0); //
  439.   CopyBSTRToCString( bstr, &ansi );
  440.  
  441.   lResult = (ansi.GetLength() + 8) & 0xFFFFFFF8;
  442.   lpResult = strResult.GetBufferSetLength( lResult );
  443.   lpAnsi = (LPCTSTR)ansi;
  444.  
  445.   if( !lpResult )
  446.     ThrowError( CTL_E_OUTOFMEMORY );
  447.  
  448.   l = ansi.GetLength();
  449.   i = 0;
  450.   while( i < l  )
  451.   {
  452.     if( (l-i) >= 8 )    
  453.     {
  454.       memcpy( dwOrg, &lpAnsi[i], 8);
  455.       i+=8;
  456.     }
  457.     else
  458.     {
  459.       dwOrg[0]=0;
  460.       dwOrg[1]=0;
  461.       char *c = (char*)dwOrg;
  462.       for( ; i < l; i++ )
  463.         *(c++) = lpAnsi[i];
  464.     }
  465.  
  466.     Encryptor.Codec( dwOrg, (DWORD*)lpResult );
  467.     lpResult += 8;
  468.   }
  469.   strResult.ReleaseBuffer( lResult );
  470.     return strResult.AllocSysString();
  471. }
  472.  
  473. BSTR CDESCtrl::DecryptString(const VARIANT FAR& Crypttext) 
  474. {
  475.     CString strResult;
  476.   CString ansi;
  477.   BSTR bstr;
  478.   LPSTR lpResult;
  479.   LPCTSTR lpAnsi;
  480.   DWORD dwOrg[2];
  481.   long l, i, lResult;
  482.  
  483.   if( !bKeySet )
  484.     ThrowError(0);
  485.  
  486.   if( !GetString( &Crypttext, &bstr ) )
  487.     ThrowError(0); //
  488.   CopyBSTRToCString( bstr, &ansi );
  489.  
  490.   lResult = (ansi.GetLength() + 8) & 0xFFFFFFF8;
  491.   lpResult = strResult.GetBufferSetLength( lResult );
  492.   lpAnsi = (LPCTSTR)ansi;
  493.  
  494.   if( !lpResult )
  495.     ThrowError( CTL_E_OUTOFMEMORY );
  496.  
  497.   l = ansi.GetLength();
  498.   i = 0;
  499.   while( i < l  )
  500.   {
  501.     if( (l-i) >= 8 )    
  502.     {
  503.       memcpy( dwOrg, &lpAnsi[i], 8);
  504.       i+=8;
  505.     }
  506.     else
  507.     {
  508.       dwOrg[0]=0;
  509.       dwOrg[1]=0;
  510.       char *c = (char*)dwOrg;
  511.       for( ; i < l; i++ )
  512.         *(c++) = lpAnsi[i];
  513.     }
  514.  
  515.     Decryptor.Codec( dwOrg, (DWORD*)lpResult );
  516.     lpResult += 8;
  517.   }
  518.   strResult.ReleaseBuffer( lResult );
  519.     return strResult.AllocSysString();
  520. }
  521.  
  522. VARIANT CDESCtrl::GetKey() 
  523. {
  524.     VARIANT vaResult;
  525.     VariantInit(&vaResult);
  526.   vaResult.vt = VT_BSTR;    
  527.   vaResult.bstrVal = m_Key.AllocSysString();
  528.     return vaResult;
  529. }
  530.  
  531. void CDESCtrl::SetKey(const VARIANT FAR& newValue) 
  532. {
  533.     BSTR bstr;
  534.   CString cstr;
  535.   DWORD dwKey[2];
  536.   if( !GetString( &newValue, &bstr ) )
  537.     ThrowError( CUSTOM_CTL_SCODE( 7000 ), "Key mu▀ als String angegeben werden!");
  538.   CopyBSTRToCString( bstr, &cstr );
  539.   if( cstr.GetLength() != 8 )
  540.     ThrowError( CUSTOM_CTL_SCODE( 7000 ), "Key mu▀ aus genau 8 Zeichen bestehen");
  541.   
  542.   memcpy( dwKey, (LPCTSTR)cstr, 8 );
  543.   Encryptor.BuildEncryptionKey( dwKey );
  544.   Decryptor.BuildDecryptionKey( dwKey );
  545.   m_Key = cstr;
  546.   bKeySet = TRUE;
  547.     SetModifiedFlag();
  548. }
  549.  
  550. BOOL CDESCtrl::OnSetExtent( LPSIZEL lpSizeL )
  551. {
  552.   SIZE s;
  553.   CDC dc;
  554.     
  555.   if( dc.CreateCompatibleDC( NULL ) )
  556.   {
  557.     s.cx = lpSizeL->cx;
  558.     s.cy = lpSizeL->cy;
  559.     dc.HIMETRICtoDP( &s );
  560.     
  561.     if( ( s.cx != 28 ) || ( s.cy != 28 ) )
  562.     {
  563.       SetControlSize( 28, 28 );
  564.       return FALSE;
  565.     }
  566.     dc.DeleteDC();
  567.   }
  568.   return TRUE;
  569. }
  570.