sl@0: /* sl@0: * Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: * All rights reserved. sl@0: * This component and the accompanying materials are made available sl@0: * under the terms of the License "Eclipse Public License v1.0" sl@0: * which accompanies this distribution, and is available sl@0: * at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: * sl@0: * Initial Contributors: sl@0: * Nokia Corporation - initial contribution. sl@0: * sl@0: * Contributors: sl@0: * sl@0: * Description: sl@0: * sl@0: */ sl@0: sl@0: sl@0: #include "sntpclientengine.h" sl@0: #include "util.h" sl@0: sl@0: // 40 second timeout on operations sl@0: #define SNTP_ENGINE_TIMEOUT 40000000 sl@0: sl@0: // NTP port sl@0: sl@0: #define SNTP_REMOTE_PORT 123 sl@0: sl@0: _LIT(KNTPEpochDate,"19000000:"); sl@0: sl@0: /* The simplest possible NTP request */ sl@0: sl@0: static const TUint8 sntpRequest[48] = { sl@0: 0x23, 0x00, 0x00, 0x00, sl@0: 0x00, 0x00, 0x00, 0x00, sl@0: 0x00, 0x00, 0x00, 0x00, sl@0: 0x00, 0x00, 0x00, 0x00, sl@0: 0x00, 0x00, 0x00, 0x00, sl@0: 0x00, 0x00, 0x00, 0x00, sl@0: 0x00, 0x00, 0x00, 0x00, sl@0: 0x00, 0x00, 0x00, 0x00, sl@0: 0x00, 0x00, 0x00, 0x00, sl@0: 0x00, 0x00, 0x00, 0x00, sl@0: 0x00, 0x00, 0x00, 0x00, sl@0: 0x00, 0x00, 0x00, 0x00 }; sl@0: sl@0: /* The main engine of the SNTP client */ sl@0: sl@0: CSNTPClient* CSNTPClient::NewL(TCommandLineArgs& aArgs) sl@0: { sl@0: CSNTPClient* self = CSNTPClient::NewLC(aArgs); sl@0: CleanupStack::Pop(self); sl@0: return self; sl@0: } sl@0: sl@0: CSNTPClient* CSNTPClient::NewLC(TCommandLineArgs& aArgs) sl@0: { sl@0: CSNTPClient* self = new (ELeave) CSNTPClient(aArgs); sl@0: CleanupStack::PushL(self); sl@0: self->ConstructL(); sl@0: return self; sl@0: } sl@0: sl@0: TSNTPClientState CSNTPClient::State() sl@0: { sl@0: return iState; sl@0: } sl@0: sl@0: void CSNTPClient::Start() sl@0: { sl@0: sl@0: iState = EStateResolve; sl@0: iResolver.GetByName(*(iArgs.iServers[iServerIndex]), iNameEntry, iStatus); sl@0: SetActive(); sl@0: iTimer->After(SNTP_ENGINE_TIMEOUT); sl@0: sl@0: } sl@0: sl@0: CSNTPClient::~CSNTPClient() sl@0: { sl@0: Cancel(); sl@0: iResolver.Close(); sl@0: iSock.Close(); sl@0: iSockServ.Close(); sl@0: sl@0: delete iTimer; sl@0: } sl@0: sl@0: CSNTPClient::CSNTPClient(TCommandLineArgs& aArgs) sl@0: : CActive(EPriorityStandard), iArgs(aArgs) sl@0: { sl@0: } sl@0: sl@0: void CSNTPClient::ConstructL() sl@0: { sl@0: User::LeaveIfError(iSockServ.Connect()); sl@0: User::LeaveIfError(iSock.Open(iSockServ, KAfInet, KSockDatagram, KProtocolInetUdp)); sl@0: User::LeaveIfError(iResolver.Open(iSockServ, KAfInet, KProtocolInetUdp)); sl@0: sl@0: iTimer = CTimeOutTimer::NewL(EPriorityHigh, *this); sl@0: CActiveScheduler::Add(this); sl@0: } sl@0: sl@0: void CSNTPClient::RunL() sl@0: { sl@0: sl@0: if (iStatus.Int() < 0) sl@0: { sl@0: User::Leave(iStatus.Int()); sl@0: } sl@0: sl@0: switch (iState) sl@0: { sl@0: sl@0: case EStateResolve: sl@0: iTimer->Cancel(); sl@0: iBuffer.Zero(); sl@0: iBuffer.Append(sntpRequest, 48); sl@0: sl@0: // set the port on the address sl@0: iNameEntry().iAddr.SetPort(SNTP_REMOTE_PORT); sl@0: sl@0: iState = EStateWrite; sl@0: iSock.SendTo(iBuffer, iNameEntry().iAddr, 0, iStatus); sl@0: SetActive(); sl@0: iTimer->After(SNTP_ENGINE_TIMEOUT); sl@0: break; sl@0: sl@0: case EStateWrite: sl@0: iTimer->Cancel(); sl@0: iState = EStateRead; sl@0: iBuffer.Zero(); sl@0: iSock.RecvFrom(iBuffer, iNameEntry().iAddr, 0, iStatus); sl@0: SetActive(); sl@0: iTimer->After(SNTP_ENGINE_TIMEOUT); sl@0: break; sl@0: sl@0: case EStateRead: sl@0: { sl@0: iTimer->Cancel(); sl@0: SetTimeL(); sl@0: iStatus = KRequestPending; sl@0: iState = EStateComplete; sl@0: TRequestStatus* status = &iStatus; sl@0: SetActive(); sl@0: User::RequestComplete(status, KErrNone); sl@0: break; sl@0: } sl@0: sl@0: case EStateComplete: sl@0: CActiveScheduler::Stop(); sl@0: // done sl@0: break; sl@0: sl@0: default: sl@0: // wuh oh. BC break! sl@0: User::Leave(KErrArgument); sl@0: break; sl@0: sl@0: } sl@0: sl@0: } sl@0: sl@0: void CSNTPClient::DoCancel() sl@0: { sl@0: sl@0: iTimer->Cancel(); sl@0: sl@0: switch (iState) sl@0: { sl@0: case EStateResolve: sl@0: iResolver.Cancel(); sl@0: break; sl@0: case EStateWrite: sl@0: iSock.CancelSend(); sl@0: break; sl@0: case EStateRead: sl@0: iSock.CancelRecv(); sl@0: break; sl@0: } sl@0: sl@0: } sl@0: sl@0: sl@0: TInt CSNTPClient::RunError(TInt /* aError */) sl@0: { sl@0: // The current server didn't work, lets try the next if available. sl@0: iTimer->Cancel(); sl@0: sl@0: if (++iServerIndex < iArgs.iServers.Count()) sl@0: { sl@0: Start(); sl@0: } sl@0: else sl@0: { sl@0: iState = EStateFailed; sl@0: CActiveScheduler::Stop(); sl@0: } sl@0: return KErrNone; sl@0: } sl@0: sl@0: void CSNTPClient::TimerExpired() sl@0: { sl@0: Cancel(); sl@0: sl@0: // The current server didn't work, lets try the next if available. sl@0: sl@0: if (++iServerIndex < iArgs.iServers.Count()) sl@0: { sl@0: Start(); sl@0: } sl@0: else sl@0: { sl@0: iState = EStateAborted; sl@0: CActiveScheduler::Stop(); sl@0: } sl@0: } sl@0: sl@0: void CSNTPClient::SetTimeL() sl@0: { sl@0: sl@0: TUint32 timestamp(0); sl@0: sl@0: /* Use the seconds from the transmit time field sl@0: sl@0: */ sl@0: sl@0: for (TInt i = 40; i < 44; ++i) sl@0: { sl@0: timestamp = (timestamp << 8) + iBuffer[i]; sl@0: } sl@0: sl@0: // Obtain the time, including the specified timezone offset sl@0: sl@0: TTimeIntervalMinutes mins(timestamp / 60); sl@0: TTimeIntervalSeconds secs(timestamp % 60); sl@0: sl@0: TTime ntpTime; sl@0: User::LeaveIfError(ntpTime.Set(KNTPEpochDate)); sl@0: ntpTime += mins; sl@0: ntpTime += secs; sl@0: sl@0: // Apply offset and (possibly) daylight savings time sl@0: sl@0: TTimeIntervalHours hours; sl@0: sl@0: if (iArgs.iApplyDaylightSavings && Util::DaylightSavingsAppliesL(ntpTime)) sl@0: { sl@0: hours = iArgs.iOffset + 1; sl@0: } sl@0: else sl@0: { sl@0: hours = iArgs.iOffset; sl@0: } sl@0: sl@0: ntpTime += hours; sl@0: sl@0: User::LeaveIfError(User::SetHomeTime(ntpTime)); sl@0: sl@0: } sl@0: sl@0: sl@0: /* Timeout handler for read/write operations */ sl@0: sl@0: CTimeOutTimer::CTimeOutTimer(const TInt aPriority) sl@0: : CTimer(aPriority) sl@0: { sl@0: } sl@0: sl@0: CTimeOutTimer::~CTimeOutTimer() sl@0: { sl@0: Cancel(); sl@0: } sl@0: sl@0: CTimeOutTimer* CTimeOutTimer::NewL(const TInt aPriority, MTimeOutNotify& aTimeOutNotify) sl@0: { sl@0: CTimeOutTimer *p = new (ELeave) CTimeOutTimer(aPriority); sl@0: CleanupStack::PushL(p); sl@0: p->ConstructL(aTimeOutNotify); sl@0: CleanupStack::Pop(); sl@0: return p; sl@0: } sl@0: sl@0: void CTimeOutTimer::ConstructL(MTimeOutNotify &aTimeOutNotify) sl@0: { sl@0: iNotify=&aTimeOutNotify; sl@0: CTimer::ConstructL(); sl@0: CActiveScheduler::Add(this); sl@0: } sl@0: sl@0: void CTimeOutTimer::RunL() sl@0: // Timer request has completed, so notify the timer's owner sl@0: { sl@0: iNotify->TimerExpired(); sl@0: }