Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

527 lines
20 KiB

  1. /*++
  2. Copyright (c) 1994 Microsoft Corporation
  3. Module Name :
  4. smtpout.hxx
  5. Abstract:
  6. This module defines the SMTP_CONNOUT class.
  7. This object maintains information about a new client connection
  8. Author:
  9. Rohan Phillips ( Rohanp ) 11-Dec-1995
  10. Project:
  11. SMTP Server DLL
  12. Revision History:
  13. --*/
  14. #ifndef _SMTP_OUT_HXX_
  15. #define _SMTP_OUT_HXX_
  16. /************************************************************
  17. * Include Headers
  18. ************************************************************/
  19. #include "smtpcli.hxx"
  20. #include "conn.hxx"
  21. #include "simssl2.h"
  22. //
  23. // Protocl Events
  24. //
  25. #include "pe_out.hxx"
  26. #include <smtpevent.h>
  27. #include "pe_supp.hxx"
  28. #include "pe_disp.h"
  29. //
  30. // Forward for IEventRouter
  31. //
  32. interface IEventRouter;
  33. //
  34. // Redefine the type to indicate that this is a call-back function
  35. //
  36. typedef ATQ_COMPLETION PFN_ATQ_COMPLETION;
  37. /************************************************************
  38. * Symbolic Constants
  39. ************************************************************/
  40. #define SIZE_OPTION 0x00000001
  41. #define PIPELINE_OPTION 0x00000002
  42. #define EBITMIME_OPTION 0x00000004
  43. #define SMARTHOST_OPTION 0x00000008
  44. #define DSN_OPTION 0x00000010
  45. #define TLS_OPTION 0x00000020
  46. #define AUTH_NTLM 0x00000040
  47. #define AUTH_CLEARTEXT 0x00000080
  48. #define ETRN_SENT 0x00000100
  49. #define ETRN_OPTION 0x00000200
  50. #define SASL_OPTION 0x00000400
  51. #define CHUNKING_OPTION 0x00000800
  52. #define BINMIME_OPTION 0x00001000
  53. #define ENHANCEDSTATUSCODE_OPTION 0x00002000
  54. #define AUTH_GSSAPI 0x00004000
  55. #define AUTH_DIGEST 0x00008000
  56. #define ETRN_ONLY_OPTION 0x00010000
  57. #define STARTTLS_OPTION 0x00020000
  58. #define EMPTY_CONNECTION_OPTION 0x00040000
  59. #define TURN_ONLY_OPTION 0x00080000
  60. const DWORD SMTP_MAX_REPLY_LENGTH = 4096;
  61. const char CONTINUATION_CHAR = (char) '-';
  62. #define SMTP_PRELIMINARY_SUCCESS (char) '1'
  63. #define SMTP_COMPLETE_SUCCESS (char) '2'
  64. #define SMTP_INTERMEDIATE_SUCCESS (char) '3'
  65. #define SMTP_TRANSIENT_FAILURE (char) '4'
  66. #define SMTP_COMPLETE_FAILURE (char) '5'
  67. #define KNOWN_AUTH_FLAGS ((DWORD)(DOMAIN_INFO_USE_NTLM | DOMAIN_INFO_USE_PLAINTEXT | DOMAIN_INFO_USE_DPA \
  68. | DOMAIN_INFO_USE_KERBEROS))
  69. enum RSETCODE
  70. {
  71. NO_SMTP_ERROR, BETWEEN_MSG, NO_RCPTS_SENT, ALL_RCPTS_FAILED, FATAL_ERROR
  72. };
  73. /************************************************************
  74. * Type Definitions
  75. ************************************************************/
  76. enum FORMAT_SMTP_MESSAGE_LOGLEVEL {
  77. FSM_LOG_ALL,
  78. FSM_LOG_VERB_ONLY,
  79. FSM_LOG_NONE
  80. };
  81. enum TLS_ACTIONS {DONT_DO_TLS, MUST_DO_TLS, STARTTLS_SENT, SSL_NEG, CHANNEL_SECURE};
  82. class SMTP_CONNOUT;
  83. /*++
  84. class SMTP_CONNOUT
  85. This class is used for keeping track of individual client
  86. connections established with the server.
  87. It maintains the state of the connection being processed.
  88. --*/
  89. class SMTP_CONNOUT :
  90. public CLIENT_CONNECTION
  91. {
  92. public:
  93. enum LASTIOSTATE {READIO, WRITEIO, TRANSFILEIO};
  94. static CPool Pool;
  95. // override the mem functions to use CPool functions
  96. void *operator new (size_t cSize)
  97. { return Pool.Alloc(); }
  98. void operator delete (void *pInstance)
  99. { Pool.Free(pInstance); }
  100. ~SMTP_CONNOUT(void);
  101. VOID DisconnectClient( IN DWORD dwErrorCode = NO_ERROR);
  102. BOOL SendSmtpResponse(BOOL SyncSend = TRUE);
  103. virtual BOOL ProcessClient( IN DWORD cbWritten,
  104. IN DWORD dwCompletionStatus,
  105. IN OUT OVERLAPPED * lpo);
  106. BOOL IsNTLMSupported(void) {return ((m_Flags & AUTH_NTLM) == AUTH_NTLM);}
  107. BOOL IsClearTextSupported(void) {return ((m_Flags & AUTH_CLEARTEXT) == AUTH_CLEARTEXT);}
  108. BOOL IsUsingSASL(void) {return ((m_Flags & SASL_OPTION) == SASL_OPTION);}
  109. void SetCarrierOption(DWORD NewOption){ m_Flags |= NewOption;}
  110. void SetCurrentObjectToNull(void) { m_pISMTPConnection = NULL;}
  111. void SetCurrentObject(ISMTPConnection * pISMTPConnection)
  112. {
  113. m_pISMTPConnection = pISMTPConnection;
  114. GetSessionPropertiesFromAQ();
  115. CopyRemoteIPAddressToSession();
  116. }
  117. BOOL SetDnsRecToNextMx();
  118. BOOL ConnectToNextIpAddress(void);
  119. void SetConnectedDomain(const char * RealDomain)
  120. {
  121. lstrcpyn(m_ConnectedDomain, RealDomain, MAX_INTERNET_NAME);
  122. }
  123. virtual BOOL StartSession( VOID);
  124. BOOL ReStartSession(void);
  125. BOOL IsSecure (void) const {return m_UsingSSL;}
  126. BOOL DoEHLOCommand(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  127. BOOL DoRSETCommand(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  128. BOOL DoQUITCommand(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  129. BOOL DoSTARTTLSCommand(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  130. BOOL DoSASLCommand(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  131. BOOL DoMAILCommand(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  132. BOOL DoRCPTCommand(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  133. BOOL DoDATACommand(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  134. BOOL DoBDATCommand(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  135. BOOL DoSSLNegotiation(char *InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  136. BOOL DoSASLNegotiation(char *InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  137. BOOL DoTURNCommand(char *InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  138. BOOL WaitForRSETResponse(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  139. BOOL WaitForQuitResponse(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  140. BOOL WaitForConnectResponse(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  141. BOOL ProcessReadIO (IN DWORD InputBufferLen, IN DWORD dwCompletionStatus, IN OUT OVERLAPPED * lpo);
  142. BOOL ProcessFileIO (IN DWORD InputBufferLen, IN DWORD dwCompletionStatus, IN OUT OVERLAPPED * lpo);
  143. BOOL ProcessWriteIO (IN DWORD InputBufferLen, IN DWORD dwCompletionStatus, IN OUT OVERLAPPED * lpo);
  144. BOOL ProcessTransmitFileIO (IN DWORD InputBufferLen, IN DWORD dwCompletionStatus, IN OUT OVERLAPPED * lpo);
  145. BOOL DoDATACommandEx(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  146. BOOL DoETRNCommand(void);
  147. BOOL FormatSmtpMessage(FORMAT_SMTP_MESSAGE_LOGLEVEL eLogLevel,
  148. IN const char * Format, ...);
  149. BOOL FormatBinaryBlob(IN PBYTE pbBlob, IN DWORD cbSize);
  150. //BOOL PipelineRecipients(void);
  151. BOOL AddRcptsDsns(DWORD NotifyOptions, char * OrcptVal, char * AddrBuf, int& AddrSize);
  152. //BOOL CheckPipelinedAnswers (char * Buffer, DWORD BufSize, LPDWORD LastSize, DWORD UndecryptedTailSize);
  153. char * GetConnectedDomain(void) const {return (char *) m_ConnectedDomain;}
  154. BOOL GetConnectionStatus(void) const {return m_Active;}
  155. BOOL TransmitFileEx (HANDLE hFile, LARGE_INTEGER &liSize,
  156. DWORD Offset, LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers);
  157. BOOL GoToWaitForConnectResponseState(void);
  158. BOOL GetNextTURNConnection(void);
  159. BOOL UnMarkHandledRcpts(void);
  160. PSMTP_SERVER_INSTANCE QuerySmtpInstance( VOID ) const
  161. { _ASSERT(m_pInstance != NULL); return m_pInstance; }
  162. BOOL IsSmtpInstance( VOID ) const
  163. { return m_pInstance != NULL; }
  164. VOID SetSmtpInstance( IN PSMTP_SERVER_INSTANCE pInstance )
  165. { _ASSERT(m_pInstance == NULL); m_pInstance = pInstance; }
  166. void SetTurnList(IN PTURN_DOMAIN_LIST pTurnList)
  167. { m_pmszTurnList = pTurnList->pmsz; m_szCurrentTURNDomain = pTurnList->szCurrentDomain;}
  168. void ProtocolLogCommand(LPSTR pszCommand,
  169. DWORD cParameters,
  170. LPCSTR pszIpAddress,
  171. FORMAT_SMTP_MESSAGE_LOGLEVEL eLogLevel);
  172. void ProtocolLogResponse(LPSTR pszResponse,
  173. DWORD cResponse,
  174. LPCSTR pszIpAddress);
  175. void LogRemoteDeliveryTransaction(
  176. LPCSTR pszOperation,
  177. LPCSTR pszTarget,
  178. LPCSTR pszParameters,
  179. LPCSTR pszIpAddress,
  180. DWORD dwWin32Error,
  181. DWORD dwServiceSpecificStatus,
  182. DWORD dwBytesSent,
  183. DWORD dwBytesRecieved,
  184. BOOL fResponse
  185. );
  186. //
  187. // These are overridden from the base CLIENT_CONNECTION class to support
  188. // switching of receive buffers. We need to be able to switch buffers to
  189. // support SSL, which needs upto 32K chunks. Note that even though SSL
  190. // V3.0 restricts fragment sizes to 16K, our schannel is capable of
  191. // generating 32K fragments (due to a bug). So, we read up to 32K.
  192. //
  193. virtual LPCSTR QueryRcvBuffer( VOID) const
  194. { return ((LPCSTR) m_precvBuffer); }
  195. virtual LPSTR QueryMRcvBuffer(VOID) // modifiable string
  196. { return (LPSTR) m_precvBuffer; }
  197. //
  198. // This method causes this object to allocate 32K buffers and use them as
  199. // the receive and output buffer.
  200. //
  201. BOOL SwitchToBigSSLBuffers();
  202. static SMTP_CONNOUT * CreateSmtpConnection (
  203. IN PSMTP_SERVER_INSTANCE pInstance,
  204. IN SOCKET sClient,
  205. IN const SOCKADDR_IN * psockAddrRemote,
  206. IN const SOCKADDR_IN * psockAddrLocal /* = NULL */ ,
  207. IN PATQ_CONTEXT pAtqContext /* = NULL */ ,
  208. IN PVOID pvInitialRequest/* = NULL*/ ,
  209. IN DWORD cbInitialData /* = 0 */ ,
  210. IN DWORD Options /* = 0 */,
  211. IN LPSTR pszSSLVerificationName,
  212. IN DNS_RESOLVER_RECORD *pDNS_RESOLVER_RECORD);
  213. //
  214. // Protocol Events additions ...
  215. //
  216. public:
  217. BOOL DoCompletedMessage(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  218. BOOL DoSessionStartEvent(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  219. BOOL DoMessageStartEvent(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  220. BOOL DoPerRecipientEvent(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  221. BOOL DoBeforeDataEvent(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  222. BOOL DoSessionEndEvent(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  223. BOOL DoEHLOResponse(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  224. BOOL DoMAILResponse(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  225. BOOL DoRCPTResponse(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  226. BOOL DoContentResponse(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize);
  227. BOOL PE_AppendSmtpMessage(IN char *Text);
  228. BOOL DecPendingIoCountEx(void);
  229. void SetDnsRec(PSMTPDNS_RECS pDnsRec) {m_pDnsRec = pDnsRec;}
  230. void SetConnectionStatus(DWORD dwConnectionStatus) {m_dwConnectionStatus = dwConnectionStatus;};
  231. static VOID PromoteSessionPropertiesToAQ(IUnknown *pISession,
  232. ISMTPConnection *pISMTPConnection);
  233. private:
  234. typedef BOOL (SMTP_CONNOUT::*PMFI)(char * InputLine, DWORD InputLineSize, DWORD UndecryptedTailSize);
  235. HRESULT GetNextResponse(
  236. char *InputLine,
  237. DWORD BufSize,
  238. char **NextInputLine,
  239. LPDWORD pRemainingBufSize,
  240. DWORD UndecryptedTailSize
  241. );
  242. HRESULT BuildCommandQEntry(
  243. LPOUTBOUND_COMMAND_Q_ENTRY *ppEntry,
  244. BOOL *pfUseNative
  245. );
  246. HRESULT OnOutboundCommandEvent(
  247. IUnknown *pServer,
  248. IUnknown *pSession,
  249. DWORD dwEventType,
  250. BOOL fRepeatLastCommand,
  251. PMFI pDefaultOutboundHandler
  252. );
  253. HRESULT OnServerResponseEvent(
  254. IUnknown *pServer,
  255. IUnknown *pSession,
  256. PMFI pDefaultResponseHandler
  257. );
  258. BOOL GlueDispatch(
  259. const char *InputLine,
  260. DWORD ParameterSize,
  261. DWORD UndecryptedTailSize,
  262. DWORD dwOutboundEventType,
  263. PMFI pDefaultOutboundHandler,
  264. PMFI pDefaultResponseHandler,
  265. LPSTR szDefaultResponseHandlerKeyword,
  266. BOOL *pfDoneWithEvent,
  267. BOOL *pfAbortEvent
  268. );
  269. IUnknown *GetSessionPropertyBag() { return((IUnknown *)(IMailMsgPropertyBag *)(&m_SessionPropertyBag)); }
  270. //
  271. // Methods that handle getting and putting properties on the AQ Session
  272. // object. The AQ session object has a longer lifespan than the SMTP
  273. // connection object (ie - only one for multple MX failovers). AQ can
  274. // provide information to sinks (from routing) and can use diagnostic
  275. // information from SMTP (like IP address of last connection attempt).
  276. //
  277. VOID GetSessionPropertiesFromAQ();
  278. VOID PromoteSessionPropertiesToAQ()
  279. {
  280. PromoteSessionPropertiesToAQ(GetSessionPropertyBag(),
  281. m_pISMTPConnection);
  282. };
  283. VOID CopyRemoteIPAddressToSession();
  284. private:
  285. // BOOL m_fRsetBetweenMessages;
  286. RSETCODE m_RsetReasonCode;
  287. BOOL m_fNativeHandlerFired;
  288. IEventRouter *m_pIEventRouter;
  289. ISmtpOutboundCommandDispatcher *m_pOutboundDispatcher;
  290. ISmtpServerResponseDispatcher *m_pResponseDispatcher;
  291. CFifoQueue m_FifoQ;
  292. COutboundContext m_OutboundContext;
  293. CResponseContext m_ResponseContext;
  294. CMailMsgPropertyBag m_SessionPropertyBag;
  295. //
  296. // End protocol events additions
  297. //
  298. private:
  299. LASTIOSTATE m_LastClientIo;
  300. char m_OutputBuffer[SMTP_MAX_REPLY_LENGTH];
  301. DWORD m_OutputBufferSize;
  302. DWORD m_cbMaxOutputBuffer;
  303. DWORD m_cbMaxRecvBuffer;
  304. DWORD m_cbParsable;
  305. DWORD m_Error;
  306. DWORD m_FileSize;
  307. DWORD m_MsgOptions;
  308. DWORD m_Flags;
  309. DWORD m_AuthToUse;
  310. DWORD m_NumRcptSent;
  311. DWORD m_NumRcptSentSaved;
  312. DWORD m_SizeOptionSize ;
  313. DWORD m_NumFailedAddrs;
  314. DWORD m_dwFileOffset;
  315. LONG m_cActiveThreads;
  316. LONG m_cPendingIoCount;
  317. DWORD m_NextAddress;
  318. DWORD m_FirstPipelinedAddress;
  319. int m_First552Address;
  320. DWORD m_NumRcpts;
  321. DWORD m_FirstAddressinCurrentMail;
  322. DWORD *m_RcptIndexList;
  323. PFIO_CONTEXT m_IMsgFileHandle;
  324. PFIO_CONTEXT m_IMsgDotStuffedFileHandle;
  325. ISMTPConnection * m_pISMTPConnection;
  326. PSMTPDNS_RECS m_pDnsRec;
  327. DNS_RESOLVER_RECORD *m_pDNS_RESOLVER_RECORD;
  328. PMFI m_NextState;
  329. BOOL m_HeloSent;
  330. BOOL m_EhloSent;
  331. BOOL m_EhloFailed;
  332. BOOL m_FirstRcpt;
  333. BOOL m_SendAgain;
  334. BOOL m_SecurePort;
  335. BOOL m_fNegotiatingSSL;
  336. BOOL m_UsingSSL;
  337. BOOL m_HaveDataResponse;
  338. BOOL m_fUseMbsCta;
  339. BOOL m_Active;
  340. BOOL m_fUseBDAT;
  341. BOOL m_bComplete;
  342. BOOL m_fCanTurn;
  343. IMailMsgProperties *m_pIMsg;
  344. IMailMsgRecipients *m_pIMsgRecips;
  345. IMailMsgBind *m_pBindInterface;
  346. PVOID m_AdvContext;
  347. char m_TransmitTailBuffer [5];
  348. char m_ConnectedDomain[MAX_INTERNET_NAME + 1];
  349. TRANSMIT_FILE_BUFFERS m_TransmitBuffers;
  350. TLS_ACTIONS m_TlsState;
  351. PSMTP_SERVER_INSTANCE m_pInstance;
  352. CEncryptCtx m_encryptCtx;
  353. CSecurityCtx m_securityCtx;
  354. HANDLE m_hFile;
  355. TCP_AUTHENT_INFO AuthInfoStruct;
  356. char m_szAuthPackage[64];
  357. CProviderPackagesInfo *m_pProviderPackagesInfo; // Configured AUTH providers (refcounted object)
  358. char *m_precvBuffer;
  359. char *m_pOutputBuffer;
  360. DWORD m_dwConnectionStatus;
  361. LPSTR m_pszSSLVerificationName;
  362. SERVEREVENT_OVERLAPPED m_SeoOverlapped;
  363. //Turn related data - pointer to a MULTISZ containing all the domain names
  364. //to be turned on this connection and a pointer into the Multisz for the
  365. //current domain being turned
  366. MULTISZ *m_pmszTurnList;
  367. const char *m_szCurrentTURNDomain;
  368. //Keeps track of current place in domains to issue ETRN for
  369. const char *m_szCurrentETRNDomain;
  370. //Connection failure diagnostic information - 2/18/99 MikeSwa
  371. //This additional information is designed to be used as user-level
  372. //diagnostic information. It is reported back to Aqueue which
  373. //will expose it via the QAPI or event logs
  374. //HRESULT can be E_FAIL, S_OK, or specific error from mc files
  375. HRESULT m_hrDiagnosticError;
  376. LPSTR m_szDiagnosticVerb; //failed protocol VERB
  377. CHAR m_szDiagnosticResponse[100]; //response from remote server
  378. //DSN related
  379. BOOL m_fNeedRelayedDSN;
  380. BOOL m_fHadHardError;
  381. BOOL m_fHadTempError;
  382. BOOL m_fHadSuccessfulDelivery;
  383. SMTP_CONNOUT(
  384. IN PSMTP_SERVER_INSTANCE pInstance,
  385. IN SOCKET sClient,
  386. IN const SOCKADDR_IN * psockAddrRemote,
  387. IN const SOCKADDR_IN * psockAddrLocal = NULL,
  388. IN PATQ_CONTEXT pAtqContext = NULL,
  389. IN PVOID pvInitialRequest = NULL,
  390. IN DWORD cbInitialData = 0);
  391. BOOL InitializeObject (
  392. DWORD Options,
  393. LPSTR pszSSLVerificationName,
  394. DNS_RESOLVER_RECORD *pDNS_RESOLVER_RECORD);
  395. BOOL DecryptInputBuffer ();
  396. BOOL MessageReadFile(void);
  397. void FreeAtqFileContext(void);
  398. BOOL GetEhloOptions(char * InputLine, DWORD ParameterSize, DWORD UndecryptedTailSize, BOOL fIsHelo);
  399. void ShrinkBuffer(char * StartPosition, DWORD SizeToMove)
  400. {
  401. MoveMemory ((void *)QueryMRcvBuffer(), StartPosition, SizeToMove);
  402. }
  403. void SetNextStateEx(PMFI NextState, DWORD Timeout)
  404. {
  405. AtqContextSetInfo(m_pAtqContext, ATQ_INFO_TIMEOUT, Timeout);
  406. m_NextState = NextState;
  407. }
  408. void SetNextState(PMFI NextState)
  409. {
  410. m_NextState = NextState;
  411. }
  412. void SetDiagnosticInfo(IN HRESULT hrDiagnosticError,
  413. IN LPCSTR szDiagnosticVerb,
  414. IN LPCSTR szDiagnosticResponse);
  415. BOOL IsOptionSet(DWORD Option) {return ((m_Flags & Option) == Option);}
  416. BOOL SaveToErrorFile(char * Buffer, DWORD BufSize);
  417. void HandleCompletedMailObj(DWORD MsgStatus, char * szExtendedStatus, DWORD cbExtendedStatus);
  418. void SendRemainingRecipients (void);
  419. LONG IncPendingIoCount(void) { return InterlockedIncrement( &m_cPendingIoCount ); }
  420. LONG DecPendingIoCount(void) { return InterlockedDecrement( &m_cPendingIoCount ); }
  421. LONG IncThreadCount(void) { return InterlockedIncrement( &m_cActiveThreads ); }
  422. LONG DecThreadCount(void) { return InterlockedDecrement( &m_cActiveThreads ); }
  423. BOOL ValidateSSLCertificate ();
  424. };
  425. #endif
  426. /************************ End of File ***********************/