terminalsecurity/SCP/SCPHistoryPlugin/src/SCPHistoryPlugin.cpp
changeset 24 bf47f3b79154
parent 22 19fb38abab1d
child 60 eb6690d0d439
equal deleted inserted replaced
22:19fb38abab1d 24:bf47f3b79154
    23 #include <SCPParamObject.h>
    23 #include <SCPParamObject.h>
    24 
    24 
    25 #include "SCPHistoryPlugin.h"
    25 #include "SCPHistoryPlugin.h"
    26 #include <SCPHistoryPluginLang.rsg>
    26 #include <SCPHistoryPluginLang.rsg>
    27 #include "SCP_IDs.h"
    27 #include "SCP_IDs.h"
    28 
    28 #include <SCPServerInterface.h>
    29 
    29 
    30 // ============================= LOCAL FUNCTIONS  =============================
    30 // ============================= LOCAL FUNCTIONS  =============================
    31 
    31 
    32 // ============================= MEMBER FUNCTIONS =============================
    32 // ============================= MEMBER FUNCTIONS =============================
    33 
    33 
    82 // 
    82 // 
    83 // 
    83 // 
    84 // Status : Approved
    84 // Status : Approved
    85 // ----------------------------------------------------------------------------
    85 // ----------------------------------------------------------------------------
    86 //    
    86 //    
    87 CSCPParamObject* CSCPHistoryPlugin::HandleEvent( TInt aID, CSCPParamObject& aParam )
    87 void CSCPHistoryPlugin :: HandleEventL( TInt aID, CSCPParamObject& aParam, CSCPParamObject& aOutParam)
    88 	{				
    88 	{				
    89 	Dprint ( ( _L( "CSCPHistoryPlugin::HandleEvent()" ) ) );
    89 	Dprint ( ( _L( "CSCPHistoryPlugin::HandleEvent()" ) ) );
    90 	// Make the ParamObject for success ack, Delete later
       
    91 	CSCPParamObject* retParams = NULL;
       
    92 	
       
    93 	TBool errRaised;
       
    94 	errRaised = EFalse;
       
    95 	
       
    96 	TBool isInvalid = EFalse;
       
    97 	
    90 	
    98 	if ( iFs == NULL )
    91 	if ( iFs == NULL )
    99 	    {
    92 	    {
   100 	    return NULL; // Eventhandler not available
    93 	    return ; // Eventhandler not available
   101 	    }	
    94 	    }	
   102 	
    95 		// check for Case
   103 	// check for Case
    96 		
   104     switch ( aID )
    97 	switch ( aID )
   105         {
    98         {
   106 
    99 
   107         case ( KSCPEventValidate ) :
   100         case ( KSCPEventValidate ) :
   108             {            
   101             {            
   109            	// Obtain the paramValue
   102            	// Obtain the paramValue
   117 				// Get the configFile's path.
   110 				// Get the configFile's path.
   118 				// If this fails, there is something badly wrong(Private folder is not there)
   111 				// If this fails, there is something badly wrong(Private folder is not there)
   119 				TInt errSCF = SetConfigFile ();
   112 				TInt errSCF = SetConfigFile ();
   120 				if (errSCF != KErrNone)
   113 				if (errSCF != KErrNone)
   121 				{
   114 				{
   122 					errRaised = ETrue;
       
   123 					break; // Break out from Case
   115 					break; // Break out from Case
   124 				}
   116 				}
   125 
   117 
   126 				// Get the historyItemCount, If the err is raised, the file is not there
   118 				// Get the historyItemCount, If the err is raised, the file is not there
   127 				// This will lead to KSCPEventPasswordChanged event and new history file will
   119 				// This will lead to KSCPEventPasswordChanged event and new history file will
   129 				TInt historyItemCounter;
   121 				TInt historyItemCounter;
   130 				TInt errHC = GetHistoryItemCount( historyItemCounter );
   122 				TInt errHC = GetHistoryItemCount( historyItemCounter );
   131 				Dprint ( ( _L( "CSCPHistoryPlugin::HandleEvent historyItemCounter = %d" ), historyItemCounter ) );
   123 				Dprint ( ( _L( "CSCPHistoryPlugin::HandleEvent historyItemCounter = %d" ), historyItemCounter ) );
   132 				if (errHC != KErrNone)
   124 				if (errHC != KErrNone)
   133 				{
   125 				{
   134 					errRaised = ETrue;
       
   135 					break; // Break out from Case
   126 					break; // Break out from Case
   136 				}
   127 				}
   137 
   128 
   138 				// continue with the KSCPEventValidate Check
   129 				// continue with the KSCPEventValidate Check
   139 
   130 
   142 				if ( aParam.Get( KSCPParamPassword, seccode ) != KErrNone )
   133 				if ( aParam.Get( KSCPParamPassword, seccode ) != KErrNone )
   143 				{
   134 				{
   144 					// Nothing to do anymore
   135 					// Nothing to do anymore
   145 					Dprint( (_L("CSCPHistoryPlugin::HandleEvent()\
   136 					Dprint( (_L("CSCPHistoryPlugin::HandleEvent()\
   146 					ERROR: KSCPEventValidate/KSCPParamPassword is != KErrNone") ));
   137 					ERROR: KSCPEventValidate/KSCPParamPassword is != KErrNone") ));
   147 					errRaised = ETrue;
       
   148 					break; // Break out from Case
   138 					break; // Break out from Case
   149 				}            
   139 				}            
   150 				
   140 				
   151 				// Hash  the securitycode	
   141 				// Hash  the securitycode	
   152 				TBuf<KSCPPasscodeMaxLength> securityhash;
   142 				TBuf<KSCPPasscodeMaxLength> securityhash;
   172 			
   162 			
   173 				// If for some reason err is raised, break out
   163 				// If for some reason err is raised, break out
   174 				// If the Historyonfig file get deleted on the fly ex
   164 				// If the Historyonfig file get deleted on the fly ex
   175 				if (errGH != KErrNone)
   165 				if (errGH != KErrNone)
   176 				    {
   166 				    {
   177 					errRaised = ETrue;
       
   178 					array->Reset();
   167 					array->Reset();
   179 					delete array;
   168 					delete array;
   180 					break; // Break out from Case
   169 					break; // Break out from Case
   181 				    }
   170 				    }
   182 				TInt correction;
   171 				TInt correction;
   183 				correction = 0;
   172 				correction = 0;
   184 
   173 
   185 				if ( array->Count() >=  passhistoryParamValue )
   174 				if ( array->Count() >=  passhistoryParamValue )
   186         {
   175                 {
   187         correction =  array->Count() - passhistoryParamValue;
   176                     correction =  array->Count() - passhistoryParamValue;
   188         }
   177                 }
   189 				// check for match
   178 				// check for match
   190 				TBuf<KSCPPasscodeMaxLength> arrayItem;
   179 				TBuf<KSCPPasscodeMaxLength> arrayItem;
   191 				
   180 				
   192 				// Set the historyobject
   181 				// Set the historyobject
   193 				for (TInt i= 0 + correction; i < array->Count(); i++)
   182 				for (TInt i= 0 + correction; i < array->Count(); i++)
   194 				    {
   183 				    {
   195 					arrayItem =  array->MdcaPoint(i);
   184 					arrayItem =  array->MdcaPoint(i);
   196 					if (arrayItem.Compare(securityhash) == KErrNone)
   185 					if (arrayItem.Compare(securityhash) == KErrNone)
   197 					    {
   186 					    {
   198 						// Get the filesystem for Resource
   187                             aOutParam.Set( KSCPParamStatus, KErrSCPInvalidCode );
   199 						// If fail, bail out
   188                             Dprint ( ( _L( "EDeviceLockHistoryBuffer Failed" ) ) );
   200 						TInt errgGR = GetResource();
   189                             aOutParam.AddtoFailedPolices(EDeviceLockHistoryBuffer);
   201 						if (errgGR != KErrNone)
   190                             break;
   202 						    {
   191                         } // End of compare IF
   203 							errRaised = ETrue;
   192                     } // End of For
   204 							break; // Break out from the For
   193 					
   205 						    }	
   194                 // kill the local
   206 
   195                 array->Reset();
   207 						// Prompt buf, iNote can show only 97 chars,
   196                 delete array;
   208 						// without ... markings.
   197 
   209 						HBufC* hbuf = NULL;					
   198                 } // passhistoryParamValue
   210 						
   199             break;
   211 						if ( passhistoryParamValue == 1 )
   200             } // end of KSCPEventValidate
   212 						    {
       
   213 		                    isInvalid = ETrue;
       
   214 		                    TRAP_IGNORE(
       
   215 		                        hbuf = LoadAndFormatResL( R_SET_SEC_CODE_INFO_PREVIOUS );
       
   216 		                        );
       
   217 						    }
       
   218 						else
       
   219 						    {
       
   220 		                    isInvalid = ETrue;
       
   221 		                    TRAP_IGNORE(
       
   222 		                        hbuf = LoadAndFormatResL( 
       
   223 		                            R_SET_SEC_CODE_INFO_CHECK, 
       
   224 		                            &passhistoryParamValue );
       
   225 		                        );							    
       
   226 						    }														
       
   227 
       
   228                         if ( isInvalid )
       
   229     					    {	    							
       
   230     				    	// Create the result-object to return
       
   231     					    TRAPD( err, retParams  = CSCPParamObject::NewL() );
       
   232                             
       
   233                             if ( err == KErrNone )
       
   234     					        {
       
   235         			            retParams->Set( KSCPParamStatus, KErrSCPInvalidCode );
       
   236     	    		            retParams->Set( KSCPParamAction, KSCPActionShowUI );
       
   237     		    	            retParams->Set( KSCPParamUIMode, KSCPUINote );
       
   238     			                
       
   239     			                if ( hbuf != NULL )
       
   240     			                    {
       
   241     			                    TPtr ptr = hbuf->Des();
       
   242     			                    retParams->Set( KSCPParamPromptText, ptr );
       
   243     			                    delete hbuf;
       
   244     			                    }
       
   245     					        }
       
   246     					    
       
   247     					    break;
       
   248     					    }
       
   249 																															
       
   250 					    } // End of compare IF
       
   251 				    } // End of For
       
   252 				    
       
   253 				// kill the local
       
   254 				array->Reset();
       
   255 				delete array;
       
   256 															
       
   257 			    } // passhistoryParamValue
       
   258 			else
       
   259 			    {
       
   260 				retParams = NULL;
       
   261 			    }
       
   262 
       
   263 			break;
       
   264 			} // end of KSCPEventValidate
       
   265                     
       
   266         // Someone has changed the Seccode and I need to include it to history
   201         // Someone has changed the Seccode and I need to include it to history
   267          case ( KSCPEventPasswordChanged ) :
   202          case ( KSCPEventPasswordChanged ) :
   268 			{																
   203 			{																
   269 			// Get the configFile's path.
   204 			// Get the configFile's path.
   270 			Dprint ( ( _L( "CSCPHistoryPlugin::KSCPEventPasswordChanged" ) ) );
   205 			Dprint ( ( _L( "CSCPHistoryPlugin::KSCPEventPasswordChanged" ) ) );
   271 			TInt errSCF = SetConfigFile ();
   206 			TInt errSCF = SetConfigFile ();
   272 			if (errSCF != KErrNone)
   207 			if (errSCF != KErrNone)
   273 			    {
   208 			    {
   274 				errRaised = ETrue;
       
   275 				break; // Break out from the case
   209 				break; // Break out from the case
   276 			    }
   210 			    }
   277 			
   211 			
   278 			// Get the password from the paramObject
   212 			// Get the password from the paramObject
   279      		TBuf<KSCPPasscodeMaxLength> securitycode;
   213      		TBuf<KSCPPasscodeMaxLength> securitycode;
   280             if ( aParam.Get( KSCPParamPassword, securitycode ) != KErrNone )
   214             if ( aParam.Get( KSCPParamPassword, securitycode ) != KErrNone )
   281                 {
   215                 {
   282             	// Nothing to do anymore
   216             	// Nothing to do anymore
   283                	Dprint( (_L("CSCPHistoryPlugin::HandleEvent()\
   217                	Dprint( (_L("CSCPHistoryPlugin::HandleEvent()\
   284                	ERROR: KSCPEventPasswordChanged/KSCPParamPassword is  != KErrNone") ));
   218                	ERROR: KSCPEventPasswordChanged/KSCPParamPassword is  != KErrNone") ));
   285             	errRaised = ETrue;
       
   286 				break; // Break out from the Case
   219 				break; // Break out from the Case
   287                 }          
   220                 }          
   288 
   221 
   289 			// Hash  the securitycode	
   222 			// Hash  the securitycode	
   290 			TBuf<KSCPPasscodeMaxLength> securityhash;
   223 			TBuf<KSCPPasscodeMaxLength> securityhash;
   308                     TRAPD( errWC, historyObject->WriteToFileL( iCfgFilenamepath, iFs ) );
   241                     TRAPD( errWC, historyObject->WriteToFileL( iCfgFilenamepath, iFs ) );
   309 					if ( errWC != KErrNone )
   242 					if ( errWC != KErrNone )
   310     					{
   243     					{
   311 						Dprint( (_L("CSCPHistoryPlugin::HandleEvent(): WARNING:\
   244 						Dprint( (_L("CSCPHistoryPlugin::HandleEvent(): WARNING:\
   312 						failed to write plugin configuration: %d"), errWC ));
   245 						failed to write plugin configuration: %d"), errWC ));
   313 						errRaised = ETrue;
       
   314 						break; // Break out from the Case
   246 						break; // Break out from the Case
   315 	    				}
   247 	    				}
   316 					delete historyObject;
   248 					delete historyObject;
   317 		 		    }
   249 		 		    }
   318 			    }
   250 			    }
   322 				// Append the new passwords
   254 				// Append the new passwords
   323 				TInt err = KErrNone;
   255 				TInt err = KErrNone;
   324 				TRAPD( err2, err = AppendAndWriteSecurityCodeL( securityhash  ) );
   256 				TRAPD( err2, err = AppendAndWriteSecurityCodeL( securityhash  ) );
   325 				if ( ( err != KErrNone ) || ( err2 != KErrNone ) )
   257 				if ( ( err != KErrNone ) || ( err2 != KErrNone ) )
   326 				    {
   258 				    {
   327 					errRaised = ETrue;
       
   328 					break; // Break out from the Case						
   259 					break; // Break out from the Case						
   329 				    }										
   260 				    }										
   330 			    }    					
   261 			    }    					
   331 			break;
   262 			break;
   332 			} // end of KSCPEventPasswordChanged
   263 			} // end of KSCPEventPasswordChanged
   343                 }            
   274                 }            
   344                     
   275                     
   345             // 1011
   276             // 1011
   346             if ( paramID == (RTerminalControl3rdPartySession::EPasscodeHistoryBuffer))
   277             if ( paramID == (RTerminalControl3rdPartySession::EPasscodeHistoryBuffer))
   347                 {
   278                 {
   348 				// OK, we're interested, check that the value is valid
       
   349 				TRAPD( err, retParams  = CSCPParamObject::NewL() );
       
   350 				if ( err != KErrNone )
       
   351 				    {
       
   352 				    break; // Nothing we can do
       
   353 				    }
       
   354 
       
   355 				// All of our params are TInts
   279 				// All of our params are TInts
   356 				TInt paramValue;
   280 				TInt paramValue;
   357 				if ( aParam.Get( KSCPParamValue, paramValue ) != KErrNone )
   281 				if ( aParam.Get( KSCPParamValue, paramValue ) != KErrNone )
   358 				    {
   282 				    {
   359 					retParams->Set( KSCPParamStatus, KErrGeneral );
   283 					aOutParam.Set( KSCPParamStatus, KErrGeneral );
   360 					break;
   284 					break;
   361 				    }
   285 				    }
   362             	            
   286             	            
   363 					TInt retStatus = KErrNone;
   287 					TInt retStatus = KErrNone;
   364 					switch ( paramID )
   288 					switch ( paramID )
   376 							           
   300 							           
   377 						    break;				
   301 						    break;				
   378 						    } // end of case EPasscodeHistoryBuffer
   302 						    } // end of case EPasscodeHistoryBuffer
   379 					    } // end of switch ( paramID )
   303 					    } // end of switch ( paramID )
   380 								
   304 								
   381 			        retParams->Set( KSCPParamStatus, retStatus );
   305 			        aOutParam.Set( KSCPParamStatus, retStatus );
   382                     }
   306                     }
   383 			    else
       
   384 			        {
       
   385 				    retParams = NULL;
       
   386 			        }
       
   387 			 			 
       
   388 			 break;	            
   307 			 break;	            
   389 	         } //End of KSCPEventConfigurationQuery Case
   308 	         } //End of KSCPEventConfigurationQuery Case
   390             
   309             
   391             
   310             
   392           case ( KSCPEventReset ):
   311           case ( KSCPEventReset ):
   397               
   316               
   398               break;
   317               break;
   399               }
   318               }
   400                           
   319                           
   401           } // End of  switch ( aID )
   320           } // End of  switch ( aID )
   402                              
       
   403        // Check if Any errors were raised and handle it
       
   404     if (errRaised) 
       
   405         {
       
   406         if ( retParams != NULL )
       
   407             {
       
   408             delete retParams;
       
   409             }
       
   410         retParams = NULL;
       
   411         }
       
   412        
       
   413     return retParams; 
       
   414     }
   321     }
   415 
   322 
   416 // ----------------------------------------------------------------------------
   323 // ----------------------------------------------------------------------------
   417 // CSCPHistoryPlugin::SetEventHandler
   324 // CSCPHistoryPlugin::SetEventHandler
   418 // SetEventHandler
   325 // SetEventHandler
   652 	Dprint ( ( _L( "CSCPHistoryPlugin::GetHistoryCountParamValue passcodehistorycount = %d" ), passcodehistorycount ) );
   559 	Dprint ( ( _L( "CSCPHistoryPlugin::GetHistoryCountParamValue passcodehistorycount = %d" ), passcodehistorycount ) );
   653 	return passcodehistorycount;
   560 	return passcodehistorycount;
   654     }
   561     }
   655 
   562 
   656 // ----------------------------------------------------------------------------
   563 // ----------------------------------------------------------------------------
   657 // CSCPHistoryPlugin::GetResource
       
   658 // GetResource
       
   659 // Status : Approved
       
   660 // ----------------------------------------------------------------------------
       
   661 //
       
   662 
       
   663 TInt CSCPHistoryPlugin::GetResource()
       
   664     {
       
   665  	Dprint( (_L("CSCPSpecificStringsPlugin::GetResource()") ));
       
   666 	// The resource has to be loaded manually since it is not an application.
       
   667           	
       
   668 	TFileName resourceFile;
       
   669 	resourceFile.Append( KDriveZ );
       
   670 	resourceFile.Append( SCPHistoryPluginSrcFile );
       
   671 	BaflUtils::NearestLanguageFile( *iFs, resourceFile );
       
   672 	TRAPD( err, iRf.OpenL( *iFs, resourceFile ) );
       
   673 
       
   674 	if ( err == KErrNone )
       
   675 	    {
       
   676 		TRAP( err, iRf.ConfirmSignatureL() );
       
   677     	}          
       
   678     
       
   679     return err;       
       
   680     }
       
   681 
       
   682 
       
   683 // ----------------------------------------------------------------------------
       
   684 // CSCPHistoryPlugin::LoadResourceLC
       
   685 // GetResource
       
   686 // Status : Approved
       
   687 // ----------------------------------------------------------------------------
       
   688 //
       
   689 HBufC16* CSCPHistoryPlugin::LoadResourceLC ( TInt aResId )
       
   690     {
       
   691 	Dprint( (_L("CSCPHistoryPlugin::LoadResourceLC()") ));
       
   692 
       
   693 	// load the actual resource
       
   694     HBufC8* readBuffer = iRf.AllocReadLC( aResId );
       
   695     // as we are expecting HBufC16...
       
   696     const TPtrC16 ptrReadBuffer( (TText16*) readBuffer->Ptr(),
       
   697                                  ( readBuffer->Length() + 1 ) >> 1 );
       
   698                                  
       
   699     HBufC16* textBuffer=HBufC16::NewL( ptrReadBuffer.Length() );    
       
   700     *textBuffer=ptrReadBuffer;
       
   701     CleanupStack::PopAndDestroy( readBuffer ); // readBuffer
       
   702     CleanupStack::PushL( textBuffer );
       
   703   	return textBuffer;
       
   704     }
       
   705 
       
   706 
       
   707 // ----------------------------------------------------------------------------
       
   708 // CSCPHistoryPlugin::FlushConfigFile
   564 // CSCPHistoryPlugin::FlushConfigFile
   709 // Remove all the other passwords from the file, except the last one (current)
   565 // Remove all the other passwords from the file, except the last one (current)
   710 // 
   566 // 
   711 // Status : Approved
   567 // Status : Approved
   712 // ----------------------------------------------------------------------------
   568 // ----------------------------------------------------------------------------
   751 	CleanupStack::PopAndDestroy( array );
   607 	CleanupStack::PopAndDestroy( array );
   752 	
   608 	
   753 	CleanupStack::PopAndDestroy( historyObject );
   609 	CleanupStack::PopAndDestroy( historyObject );
   754 	
   610 	
   755 	return err;			
   611 	return err;			
   756     }
   612     } 
   757 
       
   758 
       
   759 // ----------------------------------------------------------------------------
       
   760 // CSCPHistoryPlugin::LoadAndFormatResL
       
   761 // Load the given resouce, and format the string according to the TInt parameters
       
   762 // if given.
       
   763 // 
       
   764 // Status : Approved
       
   765 // ----------------------------------------------------------------------------
       
   766 //
       
   767 HBufC* CSCPHistoryPlugin::LoadAndFormatResL( TInt aResId, TInt* aParam1, TInt* aParam2 )
       
   768     {
       
   769     Dprint ( ( _L( "CSCPHistoryPlugin::LoadAndFormatResL()" ) ) );
       
   770     HBufC16* resource = NULL;
       
   771     HBufC* hbuf = NULL;
       
   772     
       
   773     resource = LoadResourceLC( aResId );
       
   774     FormatResourceString (*resource);
       
   775     TInt allocLen = 0;
       
   776     if ( aParam1 != NULL )
       
   777         {
       
   778         allocLen += KSCPMaxIntLength;
       
   779         }
       
   780     if ( aParam2 != NULL )
       
   781         {
       
   782         allocLen += KSCPMaxIntLength;
       
   783         }
       
   784                 
       
   785 	hbuf = HBufC::NewL( resource->Length() + allocLen );
       
   786 	
       
   787 	if ( ( aParam1 == NULL ) && ( aParam2 == NULL ) )
       
   788 	    {
       
   789 	    hbuf->Des().Copy( resource->Des() );
       
   790 	    }
       
   791 	else
       
   792 	    {
       
   793 	    if ( aParam1 == NULL )
       
   794 	        {
       
   795 	        hbuf->Des().Format( resource->Des(), *aParam2 );
       
   796 	        }
       
   797 	    else if ( aParam2 == NULL )
       
   798 	        {
       
   799 	        hbuf->Des().Format(resource->Des(), *aParam1 );
       
   800 	        }
       
   801 	    else
       
   802 	        {
       
   803 	        hbuf->Des().Format(resource->Des(), *aParam1, *aParam2 );
       
   804 	        }	    
       
   805 	    }
       
   806 								    
       
   807 	CleanupStack::PopAndDestroy( resource );
       
   808 	return hbuf;
       
   809     }
       
   810 
       
   811 // ----------------------------------------------------------------------------
       
   812 // CSCPHistoryPlugin::FormatResourceString
       
   813 // The buffer that is passed is formatted to have only %i as a format specifier instead of %N or %0N etc.
       
   814 // 
       
   815 // Status : Approved
       
   816 // ----------------------------------------------------------------------------
       
   817 //  
       
   818 void CSCPHistoryPlugin::FormatResourceString(HBufC16 &aResStr)
       
   819 {
       
   820 		TInt pos = 0;
       
   821 		TInt flag = 0;
       
   822         TPtr16 bufPtr = aResStr.Des();
       
   823         _LIT (mess1, "%N");
       
   824         _LIT (mess2, "%i");
       
   825         _LIT (mess3, "%0N");
       
   826         _LIT (mess4, "%1N");
       
   827                               
       
   828         while ((pos = bufPtr.Find(mess1)) !=KErrNotFound)
       
   829         {
       
   830               bufPtr.Replace(pos,2,mess2); 
       
   831               flag = 1;
       
   832               break;                    
       
   833         }
       
   834                
       
   835         if(flag == 0)
       
   836         {
       
   837               while ((pos = bufPtr.Find(mess3)) != KErrNotFound)
       
   838               {
       
   839               		bufPtr.Replace(pos,3,mess2);
       
   840               }
       
   841                		
       
   842               while ((pos = bufPtr.Find(mess4)) != KErrNotFound)
       
   843               {
       
   844                 	bufPtr.Replace(pos,3,mess2);
       
   845               }
       
   846         }	
       
   847 }
       
   848   
       
   849 // End of File
   613 // End of File