#include "private.h"
#include "offl_cpp.h"
#include "subsmgrp.h"
#include "helper.h"

#include <mluisupp.h>

#ifdef DEBUG
void    DumpTaskTrigger(TASK_TRIGGER * pTaskTrigger);
#endif  // DEBUG

// {D994B6F0-DA3C-11d1-857D-00C04FA35C89}
const GUID NOOP_SCHEDULE_COOKIE =
{ 0xd994b6f0, 0xda3c, 0x11d1, { 0x85, 0x7d, 0x0, 0xc0, 0x4f, 0xa3, 0x5c, 0x89
} };

#ifndef TASK_FLAG_RUN_ONLY_IF_LOGGED_ON
#define TASK_FLAG_RUN_ONLY_IF_LOGGED_ON        (0x2000)
#endif

const PROPSPEC c_rgPropRead[] = {
    { PRSPEC_PROPID, PID_INTSITE_SUBSCRIPTION},
    { PRSPEC_PROPID, PID_INTSITE_FLAGS},
    { PRSPEC_PROPID, PID_INTSITE_TRACKING},
    { PRSPEC_PROPID, PID_INTSITE_CODEPAGE},
};

void UpdateTimeFormat(LPTSTR tszTimeFormat, ULONG cchTimeFormat);

HRESULT WriteProperties(POOEntry pooe);
HRESULT ReadProperties(POOEBuf pBuf);

const TCHAR c_szLoadWC[] = TEXT("LoadWC");

void FixupRandomTrigger(TASK_TRIGGER *pTrigger)
{
    if (pTrigger->wRandomMinutesInterval > 0)
    {
        //  We have a random interval so we need to add a random number of minutes to it.
        //  Given the fact that all of the fields need to carry over to the next, the
        //  simplest way to do this is to flatten the start time into FILETIME, add the
        //  random minutes, and then convert back to a TASK_TRIGGER.  This let's us use
        //  Win32 APIs instead of doing all of the calendar and carry over stuff ourselves.

        SYSTEMTIME st;
        CFileTime ft;

        memset(&st, 0, sizeof(SYSTEMTIME));
        st.wYear = pTrigger->wBeginYear;
        st.wMonth = pTrigger->wBeginMonth;
        st.wDay = pTrigger->wBeginDay;
        st.wHour = pTrigger->wStartHour;
        st.wMinute = pTrigger->wStartMinute;

        SystemTimeToFileTime(&st, &ft);

        ft += ONE_MINUTE_IN_FILETIME * (__int64)Random(pTrigger->wRandomMinutesInterval);

        FileTimeToSystemTime(&ft, &st);

        pTrigger->wBeginYear = st.wYear;
        pTrigger->wBeginMonth = st.wMonth;
        pTrigger->wBeginDay = st.wDay;
        pTrigger->wStartHour = st.wHour;
        pTrigger->wStartMinute = st.wMinute;

        pTrigger->wRandomMinutesInterval = 0;
    }
}

//  Come up with a name like "MSN Recommended Schedule"
void CreatePublisherScheduleNameW(WCHAR *pwszSchedName, int cchSchedName,
                                  const TCHAR *pszName, const WCHAR *pwszName)
{
    WCHAR wszFormat[MAX_PATH];
    WCHAR wszPubName[MAX_PATH];
    const WCHAR *pwszPubName;

    ASSERT((NULL != pszName) || (NULL != pwszName));
    ASSERT((NULL != pwszSchedName) && (cchSchedName > 0));

    if (NULL == pwszName)
    {
        ASSERT(NULL != pszName);
        MyStrToOleStrN(wszPubName, ARRAYSIZE(wszPubName), pszName);
        pwszPubName = wszPubName;
    }
    else
    {
        pwszPubName = pwszName;
    }

#ifdef UNICODE
    MLLoadStringW(IDS_RECOMMENDED_SCHEDULE_FORMAT, wszFormat, ARRAYSIZE(wszFormat));
#else
    CHAR szFormat[MAX_PATH];
    MLLoadStringA(IDS_RECOMMENDED_SCHEDULE_FORMAT, szFormat, ARRAYSIZE(szFormat));
    MultiByteToWideChar(CP_ACP, 0, szFormat, -1, wszFormat, ARRAYSIZE(wszFormat));
#endif

    wnsprintfW(pwszSchedName, cchSchedName, wszFormat, pwszPubName);
}

void CreatePublisherScheduleName(TCHAR *pszSchedName, int cchSchedName,
                                 const TCHAR *pszName, const WCHAR *pwszName)
{
    WCHAR wszSchedName[MAX_PATH];

    CreatePublisherScheduleNameW(wszSchedName, ARRAYSIZE(wszSchedName),
                                 pszName, pwszName);

    MyOleStrToStrN(pszSchedName, cchSchedName, wszSchedName);
}

HICON LoadItemIcon(ISubscriptionItem *psi, BOOL bLarge)
{
    HICON hIcon = NULL;
    SUBSCRIPTIONITEMINFO sii;
    SUBSCRIPTIONCOOKIE cookie;
    HRESULT hr;

    sii.cbSize = sizeof(SUBSCRIPTIONITEMINFO);

    psi->GetCookie(&cookie);

    hr = psi->GetSubscriptionItemInfo(&sii);

    if (SUCCEEDED(hr))
    {
        ISubscriptionAgentShellExt *pSubscriptionAgentShellExt;

        hr = CoCreateInstance(sii.clsidAgent, NULL, CLSCTX_INPROC_SERVER,
                              IID_ISubscriptionAgentShellExt,
                              (void **)&pSubscriptionAgentShellExt);
        if (SUCCEEDED(hr))
        {
            hr = pSubscriptionAgentShellExt->Initialize(&cookie, L"", L"", (SUBSCRIPTIONTYPE)-1);

            if (SUCCEEDED(hr))
            {
                IExtractIcon *pExtractIcon;
                hr = pSubscriptionAgentShellExt->QueryInterface(IID_IExtractIcon,
                                                                (void **)&pExtractIcon);

                if (SUCCEEDED(hr))
                {
                    TCHAR szIconFile[INTERNET_MAX_URL_LENGTH];
                    int iIndex;
                    UINT wFlags;
                    HICON hIconScrap = NULL;
                    HICON *phIconLarge = bLarge ? &hIcon : &hIconScrap;
                    HICON *phIconSmall = bLarge ? &hIconScrap : &hIcon;

                    hr = pExtractIcon->GetIconLocation(0, szIconFile, ARRAYSIZE(szIconFile), &iIndex, &wFlags);

                    if (SUCCEEDED(hr))
                    {
                        hr = pExtractIcon->Extract(szIconFile, iIndex, phIconLarge, phIconSmall,
                                                   MAKELONG(GetSystemMetrics(SM_CXICON),
                                                            GetSystemMetrics(SM_CXSMICON)));

                        if (S_FALSE == hr)
                        {
                            hIcon = ExtractIcon(g_hInst, szIconFile, iIndex);

                            if (NULL == hIcon)
                            {
                                hr = E_FAIL;
                            }
                        }
                        else if ((NULL != hIconScrap) && (hIcon != hIconScrap))
                        {
                            DestroyIcon(hIconScrap);
                        }
                    }
                    pExtractIcon->Release();
                }
            }

            pSubscriptionAgentShellExt->Release();
        }
    }

    if (FAILED(hr))
    {
        DWORD dwChannel = 0;
        DWORD dwDesktop = 0;
        int iSize = bLarge ? GetSystemMetrics(SM_CXICON) : GetSystemMetrics(SM_CXSMICON);
        int id;
        HINSTANCE   hinstSrc;

        ReadDWORD(psi, c_szPropChannel, &dwChannel);
        ReadDWORD(psi, c_szPropDesktopComponent, &dwDesktop);

        if (dwDesktop == 1)
        {
            id = IDI_DESKTOPITEM;
            hinstSrc = MLGetHinst();
        }
        else if (dwChannel == 1)
        {
            id = IDI_CHANNEL;
            hinstSrc = g_hInst;
        }
        else
        {
            id = IDI_WEBDOC;
            hinstSrc = g_hInst;
        }

        hIcon = (HICON)LoadImage(hinstSrc, MAKEINTRESOURCE(id), IMAGE_ICON,
                                 iSize, iSize, LR_DEFAULTCOLOR);

    }

    return hIcon;
}

BOOL ScheduleCookieExists(SYNCSCHEDULECOOKIE *pSchedCookie)
{
    HRESULT hr;
    ISyncScheduleMgr *pSyncScheduleMgr;

    hr = CoInitialize(NULL);

    if (SUCCEEDED(hr))
    {
        hr = CoCreateInstance(CLSID_SyncMgr, NULL, CLSCTX_ALL,
                              IID_ISyncScheduleMgr, (void **)&pSyncScheduleMgr);

        if (SUCCEEDED(hr))
        {
            ISyncSchedule *pSyncSchedule = NULL;

            hr = pSyncScheduleMgr->OpenSchedule(pSchedCookie, 0, &pSyncSchedule);
            if (SUCCEEDED(hr))
            {
                pSyncSchedule->Release();
            }
            pSyncScheduleMgr->Release();
        }
        CoUninitialize();
    }

    return hr == S_OK;
}

BOOL IsScheduleNameInUse(TCHAR *pszSchedName)
{
    HRESULT hr;
    ISyncScheduleMgr *pSyncScheduleMgr;

    hr = CoInitialize(NULL);

    if (SUCCEEDED(hr))
    {
        hr = CoCreateInstance(CLSID_SyncMgr, NULL, CLSCTX_ALL,
                              IID_ISyncScheduleMgr, (void **)&pSyncScheduleMgr);

        if (SUCCEEDED(hr))
        {
            CWaitCursor waitCursor;
            ISyncSchedule *pSyncSchedule = NULL;
            SYNCSCHEDULECOOKIE schedCookie = GUID_NULL;
            WCHAR wszSchedName[MAX_PATH];

            MyStrToOleStrN(wszSchedName, ARRAYSIZE(wszSchedName), pszSchedName);

            hr = pSyncScheduleMgr->CreateSchedule(wszSchedName, 0,
                                                  &schedCookie, &pSyncSchedule);
            if (SUCCEEDED(hr))
            {
                pSyncSchedule->Release();
            }
            pSyncScheduleMgr->Release();
        }
        CoUninitialize();
    }

    return hr == SYNCMGR_E_NAME_IN_USE;
}

struct CONFLICT_DATA
{
    TCHAR szSchedName[MAX_PATH];
    TCHAR szFriendlyTrigger[MAX_PATH];
};

INT_PTR CALLBACK SchedConflictDlgProc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    BOOL bResult = FALSE;

    switch (message)
    {
        case WM_INITDIALOG:
        {
            TCHAR szConflictFormat[MAX_PATH];
            TCHAR szConflictMsg[MAX_PATH * 2];
            CONFLICT_DATA *pcd = (CONFLICT_DATA *)lParam;

            ASSERT(NULL != pcd);

            MLLoadString(IDS_SCHED_CONFLICT_FORMAT,
                       szConflictFormat, ARRAYSIZE(szConflictFormat));

            wnsprintf(szConflictMsg, ARRAYSIZE(szConflictMsg),
                      szConflictFormat, pcd->szSchedName);

            SetDlgItemText(hdlg, IDC_SCHEDULE_MESSAGE, szConflictMsg);
            SetDlgItemText(hdlg, IDC_FRIENDLY_SCHEDULE_TEXT, pcd->szFriendlyTrigger);

            bResult = TRUE;
            break;
        }

        case WM_COMMAND:
            if (HIWORD(wParam) == BN_CLICKED)
            {
                EndDialog(hdlg, LOWORD(wParam));
                bResult = TRUE;
            }
            break;
    }

    return bResult;
}

BOOL CompareTaskTrigger(TASK_TRIGGER *pTrigA, TASK_TRIGGER *pTrigB)
{
    BOOL bDontMatch;    //  TRUE if any elements don't match

    //  Simple memcmp won't work since the start date will be different
    //  when in fact they are effectively the same schedule - at least
    //  from a user perspective.

    //  FEATURE - this is far from complete - we only check for values
    //  which can be set by our wizard.

    bDontMatch  = pTrigA->wStartHour != pTrigB->wStartHour;
    bDontMatch |= pTrigA->wStartMinute != pTrigB->wStartMinute;
    bDontMatch |= pTrigA->TriggerType != pTrigB->TriggerType;
    bDontMatch |= pTrigA->Type.Daily.DaysInterval != pTrigB->Type.Daily.DaysInterval;
    bDontMatch |= pTrigA->MinutesDuration != pTrigB->MinutesDuration;
    bDontMatch |= pTrigA->MinutesInterval != pTrigB->MinutesInterval;
    bDontMatch |= pTrigA->wRandomMinutesInterval != pTrigB->wRandomMinutesInterval;

    return !bDontMatch;
}


//  HandleScheduleNameConflict
//
//  Return values:
//  CONFLICT_NONE               - pSchedCookie will be GUID_NULL and the caller is
//                                free to create a new schedule
//  CONFLICT_RESOLVED_USE_NEW   - pSchedCookie will be the cookie of an existing
//                                schedule which the caller should update with
//                                it's new TASK_TRIGGER
//  CONFLICT_RESOLVED_USE_OLD   - pSchedCookie will be the cookie of an existing
//                                schedule which the caller should use without
//                                modifying anything
//  CONFLICT_UNRESOLVED         - pSchedCookie will be GUID_NULL and the caller
//                                shouldn't do anything until the user has made
//                                up his/her mind
//
int HandleScheduleNameConflict(/* in  */ TCHAR *pszSchedName,
                               /* in  */ TASK_TRIGGER *pTrigger,
                               /* in  */ HWND hwndParent,
                               /* out */ SYNCSCHEDULECOOKIE *pSchedCookie)
{
    HRESULT hr;
    ISyncScheduleMgr *pSyncScheduleMgr;
    int iResult = CONFLICT_NONE;

    *pSchedCookie = GUID_NULL;

    hr = CoInitialize(NULL);

    if (SUCCEEDED(hr))
    {
        hr = CoCreateInstance(CLSID_SyncMgr, NULL, CLSCTX_ALL,
                              IID_ISyncScheduleMgr, (void **)&pSyncScheduleMgr);

        if (SUCCEEDED(hr))
        {
            ISyncSchedule *pSyncSchedule = NULL;
            SYNCSCHEDULECOOKIE schedCookie = GUID_NULL;
            WCHAR wszSchedName[MAX_PATH];

            MyStrToOleStrN(wszSchedName, ARRAYSIZE(wszSchedName), pszSchedName);

            {
                CWaitCursor waitCursor;

                hr = pSyncScheduleMgr->CreateSchedule(wszSchedName, 0,
                                                      &schedCookie, &pSyncSchedule);
            }
            if (SUCCEEDED(hr))
            {
                pSyncSchedule->Release();
            }
            else if (SYNCMGR_E_NAME_IN_USE == hr)
            {
                //  schedCookie will have the cookie of the conflicting schedule
                hr = pSyncScheduleMgr->OpenSchedule(&schedCookie, 0, &pSyncSchedule);

                if (SUCCEEDED(hr))
                {
                    ITaskTrigger *pITaskTrigger;

                    hr = pSyncSchedule->GetTrigger(&pITaskTrigger);

                    if (SUCCEEDED(hr))
                    {
                        TASK_TRIGGER existTrigger = { sizeof(TASK_TRIGGER) };

                        hr = pITaskTrigger->GetTrigger(&existTrigger);

                        if (SUCCEEDED(hr))
                        {
                            if (!CompareTaskTrigger(&existTrigger, pTrigger))
                            {
                                CONFLICT_DATA cd;
                                LPWSTR pwszFriendlyTrigger;

                                StrCpyN(cd.szSchedName, pszSchedName, ARRAYSIZE(cd.szSchedName));
                                if (SUCCEEDED(pITaskTrigger->GetTriggerString(&pwszFriendlyTrigger)))
                                {
                                    MyOleStrToStrN(cd.szFriendlyTrigger,
                                                   ARRAYSIZE(cd.szFriendlyTrigger),
                                                   pwszFriendlyTrigger);
                                    CoTaskMemFree(pwszFriendlyTrigger);
                                }
                                else
                                {
                                    cd.szFriendlyTrigger[0] = TEXT('\0');
                                }

                                INT_PTR iRet = DialogBoxParam(MLGetHinst(),
                                                              MAKEINTRESOURCE(IDD_DUPLICATE_SCHEDULE),
                                                              hwndParent,
                                                              SchedConflictDlgProc,
                                                              (LPARAM)&cd);
                                switch (iRet)
                                {
                                    case IDC_NEW_SETTINGS:
                                        iResult = CONFLICT_RESOLVED_USE_NEW;
                                        *pSchedCookie = schedCookie;
                                        break;

                                    case IDC_OLD_SETTINGS:
                                        iResult = CONFLICT_RESOLVED_USE_OLD;
                                        *pSchedCookie = schedCookie;
                                        break;

                                    default:
                                        iResult = CONFLICT_UNRESOLVED;
                                        break;
                                }
                            }
                        }
                        pITaskTrigger->Release();
                    }
                    pSyncSchedule->Release();
                }
            }

            pSyncScheduleMgr->Release();
        }
        CoUninitialize();
    }

    return iResult;
}

HRESULT UpdateScheduleTrigger(SYNCSCHEDULECOOKIE *pSchedCookie, TASK_TRIGGER *pTrigger)
{
    HRESULT hr;
    ISyncScheduleMgr *pSyncScheduleMgr;

    hr = CoInitialize(NULL);

    if (SUCCEEDED(hr))
    {
        CWaitCursor waitCursor;
        hr = CoCreateInstance(CLSID_SyncMgr, NULL, CLSCTX_ALL,
                              IID_ISyncScheduleMgr, (void **)&pSyncScheduleMgr);

        if (SUCCEEDED(hr))
        {
            ISyncSchedule *pSyncSchedule;

            hr = pSyncScheduleMgr->OpenSchedule(pSchedCookie, 0, &pSyncSchedule);
            if (SUCCEEDED(hr))
            {
                ITaskTrigger *pITaskTrigger;

                hr = pSyncSchedule->GetTrigger(&pITaskTrigger);
                if (SUCCEEDED(hr))
                {
                    FixupRandomTrigger(pTrigger);

                    hr = pITaskTrigger->SetTrigger(pTrigger);

                    if (SUCCEEDED(hr))
                    {
                        hr = pSyncSchedule->Save();
                    }

                    pITaskTrigger->Release();
                }
                pSyncSchedule->Release();
            }
            pSyncScheduleMgr->Release();
        }
        CoUninitialize();
    }

    return hr;
}

HRESULT RemoveItemFromAllSchedules(SUBSCRIPTIONCOOKIE *pCookie)
{
    HRESULT hr;
    ISyncScheduleMgr *pSyncScheduleMgr;

    hr = CoInitialize(NULL);

    if (SUCCEEDED(hr))
    {
        CWaitCursor waitCursor;
        hr = CoCreateInstance(CLSID_SyncMgr, NULL, CLSCTX_ALL,
                              IID_ISyncScheduleMgr, (void **)&pSyncScheduleMgr);

        if (SUCCEEDED(hr))
        {
            IEnumSyncSchedules *pEnumSyncSchedules;

            hr = pSyncScheduleMgr->EnumSyncSchedules(&pEnumSyncSchedules);

            if (SUCCEEDED(hr))
            {
                SYNCSCHEDULECOOKIE schedCookie;
                ULONG ulFetched;

                while (S_OK == pEnumSyncSchedules->Next(1, &schedCookie, &ulFetched) &&
                       (0 != ulFetched))    //  this shouldn't be necessary
                {
                    ISyncSchedule *pSyncSchedule;

                    //  If this fails, there ain't much we can do about
                    //  it so just plod along anyhow

                    if (SUCCEEDED(pSyncScheduleMgr->OpenSchedule(&schedCookie, 0, &pSyncSchedule)))
                    {
                        //  Don't care about the return value, it's cheaper
                        //  for us to just delete than to ask if it's there
                        //  and then delete.
                        pSyncSchedule->SetItemCheck(CLSID_WebCheckOfflineSync,
                                                    pCookie,
                                                    SYNCMGRITEMSTATE_UNCHECKED);
                        pSyncSchedule->Save();
                        pSyncSchedule->Release();
                    }
                }
                pEnumSyncSchedules->Release();
            }
            pSyncScheduleMgr->Release();
        }
        CoUninitialize();
    }

    return hr;
}

HRESULT AddRemoveScheduledItem(SYNC_HANDLER_ITEM_INFO *pSyncHandlerItemInfo, // For Add
                               SUBSCRIPTIONCOOKIE *pCookie,                  // For Remove
                               SYNCSCHEDULECOOKIE *pSchedCookie, BOOL bAdd)
{
    HRESULT hr;
    ISyncScheduleMgr *pSyncScheduleMgr;

    ASSERT((bAdd && (NULL != pSyncHandlerItemInfo)) ||
            (!bAdd && (NULL != pCookie)));

    hr = CoInitialize(NULL);

    if (SUCCEEDED(hr))
    {
        hr = CoCreateInstance(CLSID_SyncMgr, NULL, CLSCTX_ALL,
                              IID_ISyncScheduleMgr, (void **)&pSyncScheduleMgr);

        if (SUCCEEDED(hr))
        {
            ISyncSchedule *pSyncSchedule;

            hr = pSyncScheduleMgr->OpenSchedule(pSchedCookie, 0, &pSyncSchedule);
            if (SUCCEEDED(hr))
            {
                if (bAdd)
                {

                    hr = pSyncSchedule->AddItem(pSyncHandlerItemInfo);
                    hr = pSyncSchedule->SetItemCheck(CLSID_WebCheckOfflineSync,
                                                     &pSyncHandlerItemInfo->itemID,
                                                     SYNCMGRITEMSTATE_CHECKED);
                }
                else
                {

                    hr = pSyncSchedule->SetItemCheck(CLSID_WebCheckOfflineSync,
                                                     pCookie,
                                                     SYNCMGRITEMSTATE_UNCHECKED);
                }
                hr = pSyncSchedule->Save();
                pSyncSchedule->Release();
            }
            pSyncScheduleMgr->Release();
        }
        CoUninitialize();
    }

    return hr;
}

HRESULT SetScheduleValues(ISyncSchedule *pSyncSchedule,
                          TASK_TRIGGER *pTrigger,
                          DWORD dwSyncScheduleFlags)
{
    HRESULT hr;
    ITaskTrigger *pITaskTrigger;

    ASSERT(NULL != pSyncSchedule);
    ASSERT(NULL != pTrigger);

    hr = pSyncSchedule->GetTrigger(&pITaskTrigger);

    ASSERT(SUCCEEDED(hr));

    if (SUCCEEDED(hr))
    {
        FixupRandomTrigger(pTrigger);
        hr = pITaskTrigger->SetTrigger(pTrigger);
        pITaskTrigger->Release();

        ASSERT(SUCCEEDED(hr));

        if (SUCCEEDED(hr))
        {
            DWORD dwFlags;
            DWORD dwConnectionType = SYNCSCHEDINFO_FLAGS_CONNECTION_LAN;
            WCHAR wszConnectionName[MAX_PATH];

            //  Return code doesn't help us.  This returns the best guess
            //  at connection:
            //      1) LAN
            //      2) currently connected connectoid
            //      3) auto-dial connectoid
            //  This is according to darrenmi, if this changes - kill him.
            InternetGetConnectedStateExW(&dwFlags, wszConnectionName,
                                         ARRAYSIZE(wszConnectionName), 0);

            if (dwFlags & INTERNET_CONNECTION_MODEM)
            {
                dwConnectionType = SYNCSCHEDINFO_FLAGS_CONNECTION_WAN;
            }

            hr = pSyncSchedule->SetConnection(
                (dwConnectionType == SYNCSCHEDINFO_FLAGS_CONNECTION_WAN) ?
                    wszConnectionName : NULL,
                dwConnectionType);

            ASSERT(SUCCEEDED(hr));

            if (SUCCEEDED(hr))
            {
                hr = pSyncSchedule->SetFlags(dwSyncScheduleFlags);

                ASSERT(SUCCEEDED(hr));

                if (SUCCEEDED(hr))
                {
                    hr = pSyncSchedule->Save();

                    ASSERT(SUCCEEDED(hr));
                }
            }
        }
    }

    return hr;
}

HRESULT CreateSchedule(LPWSTR pwszScheduleName, DWORD dwSyncScheduleFlags,
                       SYNCSCHEDULECOOKIE *pSchedCookie, TASK_TRIGGER *pTrigger,
                       BOOL fDupCookieOK)
{
    HRESULT hr;
    ISyncScheduleMgr *pSyncScheduleMgr;
    ISyncSchedule *pSyncSchedule = NULL;

    hr = CoInitialize(NULL);

    if (SUCCEEDED(hr))
    {
        CWaitCursor waitCursor;
        hr = CoCreateInstance(CLSID_SyncMgr, NULL, CLSCTX_ALL,
                              IID_ISyncScheduleMgr, (void **)&pSyncScheduleMgr);

        if (SUCCEEDED(hr))
        {
            hr = pSyncScheduleMgr->CreateSchedule(pwszScheduleName, 0,
                                      pSchedCookie, &pSyncSchedule);

            ASSERT((FAILED(hr) && (NULL == pSyncSchedule)) ||
                   (SUCCEEDED(hr) && (NULL != pSyncSchedule)));

            switch (hr)
            {
                case S_OK:
                    hr = SetScheduleValues(pSyncSchedule, pTrigger, dwSyncScheduleFlags);

                #ifdef DEBUG
                    if (FAILED(hr))
                    {
                        TraceMsg(TF_ALWAYS, "SetScheduleValues failed - hr=0x%08x", hr);
                    }
                #endif

                    break;

                case HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS):
                    if (fDupCookieOK)
                    {
                        hr = S_OK;
                    }
                    break;
            }

            SAFERELEASE(pSyncSchedule);

            pSyncScheduleMgr->Release();
        }
        CoUninitialize();
    }

    return hr;
}

BOOL IsCookieOnSchedule(ISyncSchedule *pSyncSchedule, SUBSCRIPTIONCOOKIE *pCookie)
{
    HRESULT hr;
    DWORD dwCheckState = SYNCMGRITEMSTATE_UNCHECKED;

    hr = pSyncSchedule->GetItemCheck(CLSID_WebCheckOfflineSync,
                                     pCookie,
                                     &dwCheckState);

    return SUCCEEDED(hr) && (SYNCMGRITEMSTATE_CHECKED & dwCheckState);
}

struct GIS_DATA
{
    SUBSCRIPTIONCOOKIE *pSubsCookie;
    SYNCSCHEDULECOOKIE *pSchedCookie;
};

BOOL GetItemScheduleCallback(ISyncSchedule *pSyncSchedule,
                             SYNCSCHEDULECOOKIE *pSchedCookie,
                             LPARAM lParam)
{
    BOOL bContinue = TRUE;
    GIS_DATA *pgd = (GIS_DATA *)lParam;

    if (IsCookieOnSchedule(pSyncSchedule, pgd->pSubsCookie))
    {
        *pgd->pSchedCookie = *pSchedCookie;
        bContinue = FALSE;
    }

    return bContinue;
}


HRESULT GetItemSchedule(SUBSCRIPTIONCOOKIE *pSubsCookie, SYNCSCHEDULECOOKIE *pSchedCookie)
{
    HRESULT hr;
    ISyncScheduleMgr *pSyncScheduleMgr;

    *pSchedCookie = GUID_NULL;

    hr = CoInitialize(NULL);

    if (SUCCEEDED(hr))
    {
        hr = CoCreateInstance(CLSID_SyncMgr, NULL, CLSCTX_ALL,
                              IID_ISyncScheduleMgr, (void **)&pSyncScheduleMgr);
        if (SUCCEEDED(hr))
        {
            ISubscriptionItem *psi;

            //  First let's chech to see if it has a custom schedule
            hr = SubscriptionItemFromCookie(FALSE, pSubsCookie, &psi);

            if (SUCCEEDED(hr))
            {
                SUBSCRIPTIONITEMINFO sii;

                sii.cbSize = sizeof(SUBSCRIPTIONITEMINFO);

                hr = psi->GetSubscriptionItemInfo(&sii);

                psi->Release();

                if (SUCCEEDED(hr) && (sii.ScheduleGroup != GUID_NULL))
                {
                    *pSchedCookie = sii.ScheduleGroup;
                }
                else
                {
                    GIS_DATA gd;

                    gd.pSubsCookie = pSubsCookie;
                    gd.pSchedCookie = pSchedCookie;
                    EnumSchedules(GetItemScheduleCallback, (LPARAM)&gd);
                }
            }


            pSyncScheduleMgr->Release();
        }
        CoUninitialize();
    }

    return hr;
}

HRESULT EnumSchedules(SCHEDULEENUMCALLBACK pCallback, LPARAM lParam)
{
    HRESULT hr;
    ISyncScheduleMgr *pSyncScheduleMgr;

    hr = CoInitialize(NULL);

    if (SUCCEEDED(hr))
    {
        CWaitCursor waitCursor;
        hr = CoCreateInstance(CLSID_SyncMgr, NULL, CLSCTX_ALL,
                              IID_ISyncScheduleMgr, (void **)&pSyncScheduleMgr);

        if (SUCCEEDED(hr))
        {
            IEnumSyncSchedules *pEnumSyncSchedules;

            hr = pSyncScheduleMgr->EnumSyncSchedules(&pEnumSyncSchedules);

            if (SUCCEEDED(hr))
            {
                SYNCSCHEDULECOOKIE schedCookie;
                ULONG ulFetched;

                while (S_OK == pEnumSyncSchedules->Next(1, &schedCookie, &ulFetched)&&
                       (0 != ulFetched))    //  this shouldn't be necessary
                {
                    ISyncSchedule *pSyncSchedule;

                    HRESULT hrTemp = pSyncScheduleMgr->OpenSchedule(&schedCookie, 0, &pSyncSchedule);
                    if (SUCCEEDED(hrTemp) && pSyncSchedule)
                    {
                        BOOL bContinue = pCallback(pSyncSchedule, &schedCookie, lParam);
                        pSyncSchedule->Release();

                        if (!bContinue)
                        {
                            hr = S_FALSE;
                            break;
                        }
                    }
                }
                pEnumSyncSchedules->Release();
            }
            pSyncScheduleMgr->Release();
        }
        CoUninitialize();
    }

    return hr;
}

SUBSCRIPTIONTYPE GetItemCategory(BOOL bDesktop, const CLSID& clsidDest)
{
    SUBSCRIPTIONTYPE st;

    if (clsidDest == CLSID_WebCrawlerAgent)
    {
        st = (!bDesktop) ? SUBSTYPE_URL : SUBSTYPE_DESKTOPURL;
    }
    else if (clsidDest == CLSID_ChannelAgent)
    {
        st = (!bDesktop) ? SUBSTYPE_CHANNEL : SUBSTYPE_DESKTOPCHANNEL;
    }
    else
    {
        st = SUBSTYPE_EXTERNAL;
    }

    return st;
}


SUBSCRIPTIONSCHEDULE GetGroup(BOOL bDesktop, const CLSID& clsidDest,
                              DWORD fChannelFlags, const NOTIFICATIONCOOKIE& groupCookie)
{


    SUBSCRIPTIONTYPE category = GetItemCategory(bDesktop, clsidDest);
    if (category == SUBSTYPE_CHANNEL || category == SUBSTYPE_DESKTOPCHANNEL) {
        if ((fChannelFlags & CHANNEL_AGENT_DYNAMIC_SCHEDULE) &&
            (GUID_NULL == groupCookie))
            return SUBSSCHED_AUTO;
    }

    //  We have no idea about the AUTO schedule stuff of unknown types.

    if (groupCookie == NOTFCOOKIE_SCHEDULE_GROUP_DAILY)
            return SUBSSCHED_DAILY;
    else if (groupCookie == NOTFCOOKIE_SCHEDULE_GROUP_WEEKLY)
        return SUBSSCHED_WEEKLY;
    else if (groupCookie == NOTFCOOKIE_SCHEDULE_GROUP_MANUAL)
        return SUBSSCHED_MANUAL;
    else
        return SUBSSCHED_CUSTOM;
}

HRESULT LoadGroupCookie(NOTIFICATIONCOOKIE * pCookie, SUBSCRIPTIONSCHEDULE subGroup)
{
    if (pCookie)    {
        switch (subGroup)   {
        case SUBSSCHED_DAILY:
            *pCookie = NOTFCOOKIE_SCHEDULE_GROUP_DAILY;
            break;
        case SUBSSCHED_WEEKLY:
            *pCookie = NOTFCOOKIE_SCHEDULE_GROUP_WEEKLY;
            break;
        case SUBSSCHED_MANUAL:
            *pCookie = NOTFCOOKIE_SCHEDULE_GROUP_MANUAL;
            break;
        default:
            *pCookie = CLSID_NULL;
            ASSERT(0);
            break;
        }
        return S_OK;
    }

    return E_INVALIDARG;
}


/////////////////////////////////////////////////////////////////////
//  Subscription helper functions.


HRESULT TSTR2BSTR(VARIANT * pvarBSTR, LPCTSTR srcTSTR)
{
    ASSERT(pvarBSTR);
    ASSERT(srcTSTR);

    BSTR    bstrBuf = NULL;
    LONG    lTSTRLen = 0;

    lTSTRLen = lstrlen(srcTSTR) + 1;
    bstrBuf = SysAllocStringLen(NULL, lTSTRLen);
    if (!bstrBuf)
        return E_OUTOFMEMORY;
    MyStrToOleStrN(bstrBuf, lTSTRLen, srcTSTR);
    pvarBSTR->vt = VT_BSTR;
    pvarBSTR->bstrVal = bstrBuf;
    return S_OK;
}

HRESULT WriteCookieToInetDB(LPCTSTR pszURL, SUBSCRIPTIONCOOKIE *pCookie, BOOL bRemove)
{
    PROPVARIANT propCookie;
    LPOLESTR pclsid = NULL; // init to keep compiler happy

    ASSERT(pszURL);
    if (bRemove)
    {
        propCookie.vt = VT_EMPTY;
    }
    else
    {
        ASSERT(pCookie);

        if (FAILED(StringFromCLSID(*pCookie, &pclsid)))
            return E_FAIL;

        propCookie.vt = VT_LPWSTR;
        propCookie.pwszVal = pclsid;
    }

    HRESULT hr = IntSiteHelper(pszURL, &c_rgPropRead[PROP_SUBSCRIPTION], &propCookie, 1, TRUE);

    if (!bRemove)
        CoTaskMemFree(pclsid);

    return hr;
}

HRESULT WritePropertiesToItem(POOEntry pooe, ISubscriptionItem *psi)
{
    HRESULT hr = S_OK;
    VARIANT var;
    BOOL bHasUNAME = TRUE;

    ASSERT(NULL != psi);

    VariantInit(&var);
    if (pooe->dwFlags & PROP_WEBCRAWL_URL)
    {
        if (FAILED(TSTR2BSTR(&var, URL(pooe))))
            return E_FAIL;
        WriteVariant(psi, c_szPropURL, &var);
        VariantClear(&var);
    }

    if (pooe->dwFlags & PROP_WEBCRAWL_NAME)
    {
        if (FAILED(TSTR2BSTR(&var, NAME(pooe))))
            return E_FAIL;
        WriteVariant(psi, c_szPropName, &var);
        VariantClear(&var);
    }

    if (pooe->dwFlags & PROP_WEBCRAWL_UNAME)
    {
        if(*(UNAME(pooe)))
        {
            if (FAILED(TSTR2BSTR(&var, UNAME(pooe))))
                return E_FAIL;
            WriteVariant(psi, c_szPropCrawlUsername, &var);
            VariantClear(&var);
        }
        else
        {
            WriteEMPTY(psi, c_szPropCrawlUsername);
            bHasUNAME = FALSE;
        }
    }

    if (pooe->dwFlags & PROP_WEBCRAWL_DESKTOP)
    {
        if (pooe->bDesktop)
        {
            WriteDWORD(psi, c_szPropDesktopComponent, 1);
        }
        else
        {
            WriteEMPTY(psi, c_szPropDesktopComponent);
        }
    }

    if (pooe->dwFlags & PROP_WEBCRAWL_CHANNEL)
    {
        if (pooe->bChannel)
        {
            WriteDWORD(psi, c_szPropChannel, 1);
        }
        else
        {
            WriteEMPTY(psi, c_szPropChannel);
        }
    }

    if (pooe->dwFlags & PROP_WEBCRAWL_GLEAM)
    {
        if (pooe->bGleam)
        {
            WriteDWORD(psi, c_szPropEnableShortcutGleam, 1);
        }
        else
        {
            WriteEMPTY(psi, c_szPropEnableShortcutGleam);
        }
    }

    if (pooe->dwFlags & PROP_WEBCRAWL_CHANGESONLY)
    {
        if (pooe->bChangesOnly)
        {
            WriteDWORD(psi, c_szPropCrawlChangesOnly, 1);
        }
        else
        {
            WriteEMPTY(psi, c_szPropCrawlChangesOnly);
        }
    }

    if (pooe->dwFlags & PROP_WEBCRAWL_CHANNELFLAGS)
    {
        if (pooe->fChannelFlags)
        {
            WriteDWORD(psi, c_szPropChannelFlags, pooe->fChannelFlags);
        }
        else
        {
            WriteEMPTY(psi, c_szPropChannelFlags);
        }
    }

    if (pooe->dwFlags & PROP_WEBCRAWL_EMAILNOTF)
    {
        if (pooe->bMail)
        {
            WriteDWORD(psi, c_szPropEmailNotf, 1);
        }
        else
        {
            WriteEMPTY(psi, c_szPropEmailNotf);
        }
    }

    if (pooe->dwFlags & PROP_WEBCRAWL_PSWD)
    {
        if (*(PASSWD(pooe)) && bHasUNAME)
        {
            if (FAILED(TSTR2BSTR(&var, PASSWD(pooe))))
                return E_FAIL;
            WritePassword(psi, var.bstrVal);
            VariantClear(&var);
        }
        else
        {
            WritePassword(psi, NULL);
        }
    }

    if (pooe->dwFlags & PROP_WEBCRAWL_LEVEL)
    {
        if(pooe->m_RecurseLevels)
        {
            WriteDWORD(psi, c_szPropCrawlLevels, pooe->m_RecurseLevels);
        }
        else
        {
            // top page only was specified, empty out levels
            WriteEMPTY(psi, c_szPropCrawlLevels);
        }
    }

    if (pooe->dwFlags & PROP_WEBCRAWL_FLAGS)
    {
        WriteDWORD(psi, c_szPropCrawlFlags, pooe->m_RecurseFlags);
    }

    if (pooe->dwFlags & PROP_WEBCRAWL_ACTUALSIZE)
    {
        WriteDWORD(psi, c_szPropCrawlActualSize, pooe->m_ActualSize);
    }

    if (pooe->dwFlags & PROP_WEBCRAWL_SIZE)
    {
        if(pooe->m_SizeLimit)
        {
            // limit was specified
            WriteDWORD(psi, c_szPropCrawlMaxSize, pooe->m_SizeLimit);
        }
        else
        {
            // no limit was specified, empty out limit prop
            WriteEMPTY(psi, c_szPropCrawlMaxSize);
        }
    }

    SUBSCRIPTIONITEMINFO sii;
    sii.cbSize = sizeof(SUBSCRIPTIONITEMINFO);
    if (SUCCEEDED(psi->GetSubscriptionItemInfo(&sii)))
    {
        sii.dwFlags = pooe->grfTaskTrigger;
        psi->SetSubscriptionItemInfo(&sii);
    }

    //  We don't write Status/Last update.

    // FEATURE: We should fail the subscription if we were unable to write
    // any of the properties into the notification for some reason.


    return hr;
}

HRESULT WriteProperties(POOEntry pooe)
{
    HRESULT hr;
    ISubscriptionItem *psi = NULL;

    ASSERT(NULL != pooe);

    hr = SubscriptionItemFromCookie(FALSE, &pooe->m_Cookie, &psi);

    if (SUCCEEDED(hr))
    {
        hr = WritePropertiesToItem(pooe, psi);
        psi->Release();
    }
    return hr;
}

HRESULT CreatePublisherSchedule()
{
    return S_OK;
}

#define RANDOM_TIME_START       0       // 12am (in minutes)
#define RANDOM_TIME_END         300     // 5am (in minutes)
#define RANDOM_TIME_INC         30      // 30min increment

DWORD GetRandomTime(DWORD StartMins, DWORD EndMins, DWORD Inc)
{
    DWORD Range;
    DWORD nIncrements;

    if (StartMins > EndMins)
    {
        Range = ((1440 - StartMins) + EndMins);
    }
    else
    {
        Range = (EndMins - StartMins);
    }

    nIncrements = ((Range / Inc) + 1);

    return (StartMins + (Random(nIncrements) * Inc));
}

HRESULT CreateDefaultSchedule(SUBSCRIPTIONSCHEDULE subsSchedule,
                              SYNCSCHEDULECOOKIE *pSchedCookie)
{
    HRESULT hr = S_OK;
    TASK_TRIGGER trig;
    int resID;

    memset((void*) &trig, 0x0, sizeof(TASK_TRIGGER));
    *pSchedCookie = GUID_NULL;

    ZeroMemory(&trig, sizeof(trig));

    switch (subsSchedule)
    {
        case SUBSSCHED_DAILY:
            trig.TriggerType = TASK_TIME_TRIGGER_DAILY;
            trig.Type.Daily.DaysInterval = 1;
            resID = IDS_DAILY_GRO;
            *pSchedCookie = NOTFCOOKIE_SCHEDULE_GROUP_DAILY;
            break;

        case SUBSSCHED_WEEKLY:
            trig.TriggerType = TASK_TIME_TRIGGER_WEEKLY;
            trig.Type.Weekly.WeeksInterval = 1;
            trig.Type.Weekly.rgfDaysOfTheWeek = TASK_MONDAY;
            resID = IDS_WEEKLY_GRO;
            *pSchedCookie = NOTFCOOKIE_SCHEDULE_GROUP_WEEKLY;
            break;

        case SUBSSCHED_AUTO:
        case SUBSSCHED_CUSTOM:
        case SUBSSCHED_MANUAL:
        default:
            resID = 0;
            hr = E_FAIL;
            break;
    }

    if (SUCCEEDED(hr))
    {
        if (!ScheduleCookieExists(pSchedCookie))
        {
            WCHAR wszSchedName[MAX_PATH];
            DWORD dwRandTime = GetRandomTime(RANDOM_TIME_START,
                                             RANDOM_TIME_END,
                                             RANDOM_TIME_INC);

            trig.cbTriggerSize = sizeof(TASK_TRIGGER);
            trig.wRandomMinutesInterval = RANDOM_TIME_INC;
            trig.wStartHour = (UINT)(dwRandTime / 60);
            trig.wStartMinute = (UINT)(dwRandTime % 60);
            trig.rgFlags = 0;

            MLLoadStringW(resID, wszSchedName, ARRAYSIZE(wszSchedName));

            hr = CreateSchedule(wszSchedName, 0, pSchedCookie, &trig, TRUE);

            if (hr == SYNCMGR_E_NAME_IN_USE)
            {
                hr = S_OK;
            }
        }
    }

    return hr;
}

HRESULT AddIt(ISubscriptionItem *psi, POOEntry pooe, SUBSCRIPTIONSCHEDULE subGroup)
{
    SYNCSCHEDULECOOKIE schedCookie = GUID_NULL;
    HRESULT hr = E_FAIL;

    switch (subGroup)
    {
        case SUBSSCHED_DAILY:
        case SUBSSCHED_WEEKLY:
            hr = CreateDefaultSchedule(subGroup, &schedCookie);
            break;

        case SUBSSCHED_CUSTOM:
            schedCookie = pooe->groupCookie;
            hr = S_OK;
            break;

        case SUBSSCHED_MANUAL:
	    SUBSCRIPTIONCOOKIE cookie;

	    cookie = pooe->m_Cookie;
            RemoveItemFromAllSchedules(&cookie);
	    pooe->m_Cookie = cookie;
            hr = S_FALSE;
            break;

        case SUBSSCHED_AUTO:
            //  FEATURE - for now, until pub schedules are wired in
            hr = CreateDefaultSchedule(SUBSSCHED_DAILY, &schedCookie);
            break;
    }

    if (hr == S_OK)
    {
        ASSERT(GUID_NULL != schedCookie);

        if (NOOP_SCHEDULE_COOKIE == schedCookie)
        {
            hr = S_FALSE;
        }
        if (GUID_NULL != schedCookie)
        {
            SYNC_HANDLER_ITEM_INFO shii;

            shii.handlerID = CLSID_WebCheckOfflineSync;
            shii.itemID = pooe->m_Cookie;
            shii.hIcon = NULL;
            MyStrToOleStrN(shii.wszItemName, ARRAYSIZE(shii.wszItemName), NAME(pooe));
            shii.dwCheckState = SYNCMGRITEMSTATE_CHECKED;

            hr = AddScheduledItem(&shii, &schedCookie);
        }
        else
        {
            hr = E_FAIL;
        }
    }

    return S_OK;
//  return hr;
}

HRESULT ScheduleIt(ISubscriptionItem *psi, TCHAR *pszName, TASK_TRIGGER *pTrigger)
{
    HRESULT hr;
    SUBSCRIPTIONITEMINFO subscriptionItemInfo;

    ASSERT(pTrigger->cbTriggerSize == sizeof(TASK_TRIGGER));

    subscriptionItemInfo.cbSize = sizeof(SUBSCRIPTIONITEMINFO);

#ifdef DEBUG
    DumpTaskTrigger(pTrigger);
#endif

    hr = psi->GetSubscriptionItemInfo(&subscriptionItemInfo);

    if (SUCCEEDED(hr))
    {
        if (GUID_NULL != subscriptionItemInfo.ScheduleGroup)
        {
            hr = UpdateScheduleTrigger(&subscriptionItemInfo.ScheduleGroup, pTrigger);
        }
        else
        {
            hr = E_FAIL;
        }
    }

    if (FAILED(hr))
    {
        WCHAR wszSchedName[MAX_PATH];

        CreatePublisherScheduleNameW(wszSchedName, ARRAYSIZE(wszSchedName),
                                     pszName, NULL);

        hr = CreateSchedule(wszSchedName, SYNCSCHEDINFO_FLAGS_READONLY,
                            &subscriptionItemInfo.ScheduleGroup, pTrigger, TRUE);

        if (SUCCEEDED(hr) || (hr == SYNCMGR_E_NAME_IN_USE))
        {
            psi->SetSubscriptionItemInfo(&subscriptionItemInfo);
            hr = S_OK;
        }
        else
        {
            TraceMsg(TF_ALWAYS, "Error creating schedule - hr=0x%08x", hr);
        }
    }

    if (SUCCEEDED(hr))
    {
        SYNC_HANDLER_ITEM_INFO shii;

        shii.handlerID = CLSID_WebCheckOfflineSync;
        psi->GetCookie(&shii.itemID);
        shii.hIcon = NULL;
        MyStrToOleStrN(shii.wszItemName, ARRAYSIZE(shii.wszItemName), pszName);

        hr = AddScheduledItem(&shii, &subscriptionItemInfo.ScheduleGroup);
    }

    return S_OK;
//    return hr;
}

HRESULT ReadProperties(POOEBuf pBuf)
{
    VARIANT var;
    HRESULT hr;
    ASSERT(pBuf);
    BOOL    bHasUNAME = TRUE;
    ISubscriptionItem *psi = NULL;

    ASSERT(NULL != pBuf);

    hr = SubscriptionItemFromCookie(FALSE, &pBuf->m_Cookie, &psi);

    if (SUCCEEDED(hr))
    {
        VariantInit(&var);
        if (pBuf->dwFlags & PROP_WEBCRAWL_URL)
        {
            hr = ReadVariant(psi, c_szPropURL, &var);
            if (SUCCEEDED(hr) && (var.vt == VT_BSTR))
            {
                MyOleStrToStrN(pBuf->m_URL, MAX_URL, var.bstrVal);
            }
            else
            {
                pBuf->m_URL[0] = (TCHAR)0;
            }
            VariantClear(&var);
        }

        if (pBuf->dwFlags & PROP_WEBCRAWL_NAME)
        {
            hr = ReadVariant(psi, c_szPropName, &var);
            if (SUCCEEDED(hr) && (var.vt == VT_BSTR))
            {
                MyOleStrToStrN(pBuf->m_Name, MAX_NAME, var.bstrVal);
            }
            else
            {
                pBuf->m_Name[0] = (TCHAR)0;
            }
            VariantClear(&var);
        }

        if (pBuf->dwFlags & PROP_WEBCRAWL_UNAME)
        {
            hr = ReadVariant(psi, c_szPropCrawlUsername, &var);
            if (SUCCEEDED(hr) && (var.vt == VT_BSTR))
            {
                MyOleStrToStrN(pBuf->username, MAX_USERNAME, var.bstrVal);
            }
            else
            {
                pBuf->username[0] = (TCHAR)0;
                bHasUNAME = FALSE;
            }
            VariantClear(&var);
        }

        if (pBuf->dwFlags & PROP_WEBCRAWL_DESKTOP)
        {
            hr = ReadVariant(psi, c_szPropDesktopComponent, &var);
            if (SUCCEEDED(hr) && (var.vt == VT_I4) && (var.lVal == 1))
            {
                pBuf->bDesktop = TRUE;
            }
            else
            {
                pBuf->bDesktop = FALSE;
            }
            VariantClear(&var);
        }

        if (pBuf->dwFlags & PROP_WEBCRAWL_CHANNEL)
        {
            hr = ReadVariant(psi, c_szPropChannel, &var);
            if (SUCCEEDED(hr) && (var.vt == VT_I4) && (var.lVal == 1))
            {
                pBuf->bChannel = TRUE;
            }
            else
            {
                pBuf->bChannel = FALSE;
            }
            VariantClear(&var);
        }

        if (pBuf->dwFlags & PROP_WEBCRAWL_GLEAM)
        {
            hr = ReadVariant(psi, c_szPropEnableShortcutGleam, &var);
            if (SUCCEEDED(hr) && (var.vt == VT_I4) && (var.lVal == 1))
            {
                pBuf->bGleam = TRUE;
            }
            else
            {
                pBuf->bGleam = FALSE;
            }
            VariantClear(&var);
        }

        if (pBuf->dwFlags & PROP_WEBCRAWL_CHANGESONLY)
        {
            hr = ReadVariant(psi, c_szPropCrawlChangesOnly, &var);
            if (SUCCEEDED(hr) && (var.vt == VT_I4) && (var.lVal == 1))
            {
                pBuf->bChangesOnly = TRUE;
            }
            else
            {
                pBuf->bChangesOnly = FALSE;
            }
            VariantClear(&var);
        }

        if (pBuf->dwFlags & PROP_WEBCRAWL_CHANNELFLAGS)
        {
            hr = ReadVariant(psi, c_szPropChannelFlags, &var);
            if (SUCCEEDED(hr) && (var.vt == VT_I4))
            {
                pBuf->fChannelFlags = var.lVal;
            }
            else
            {
                pBuf->fChannelFlags = 0;
            }
            VariantClear(&var);
        }

        if (pBuf->dwFlags & PROP_WEBCRAWL_EMAILNOTF)
        {
            hr = ReadVariant(psi, c_szPropEmailNotf, &var);
            if (SUCCEEDED(hr) && (var.vt == VT_I4) && (var.lVal == 1))
            {
                pBuf->bMail = TRUE;
            }
            else
            {
                pBuf->bMail = FALSE;
            }
            VariantClear(&var);
        }

        if ((pBuf->dwFlags & PROP_WEBCRAWL_PSWD) && bHasUNAME)
        {
            BSTR bstrVal = NULL;
            hr = ReadPassword(psi, &bstrVal);
            if (SUCCEEDED(hr) && bstrVal)
            {
                MyOleStrToStrN(pBuf->password, MAX_PASSWORD, bstrVal);
            }
            else
            {
                pBuf->password[0] = (TCHAR)0;
            }
            SAFEFREEBSTR(bstrVal);
        }

        if ((pBuf->dwFlags & PROP_WEBCRAWL_PSWD) || (pBuf->dwFlags & PROP_WEBCRAWL_UNAME)) {
            //bNeedPassword isn't stored in the property map... calculate it from the presence
            //of username/password.
            pBuf->bNeedPassword = pBuf->password[0] || pBuf->username[0];
        }

        if (pBuf->dwFlags & PROP_WEBCRAWL_STATUS)
        {
            hr = ReadVariant(psi, c_szPropStatusString, &var);
            if (SUCCEEDED(hr) && (var.vt == VT_BSTR))
            {
                MyOleStrToStrN(pBuf->statusStr, MAX_STATUS, var.bstrVal);
            }
            else
            {
                pBuf->statusStr[0] = (TCHAR)0;
            }
            VariantClear(&var);

            hr = ReadSCODE(psi, c_szPropStatusCode, &(pBuf->status));
            //  FEATURE What should we put here if we don't have last status?
            if (FAILED(hr))
            {
                pBuf->status = S_OK;
            }
            VariantClear(&var);
        }

        //
        // Use the CompletionTime property if present and it is greater than
        // value in the NOTIFICATIONITEM structure.
        //

        if (pBuf->dwFlags & PROP_WEBCRAWL_LAST)
        {
            CFileTime ft;
            hr = ReadVariant(psi, c_szPropCompletionTime, &var);
            if (SUCCEEDED(hr) && (var.vt == VT_DATE))
            {
                VariantTimeToFileTime(var.date, ft);

                if (ft > pBuf->m_LastUpdated)
                {
                    pBuf->m_LastUpdated = ft;
                }
            }
        }

        if (pBuf->dwFlags & PROP_WEBCRAWL_LEVEL)
        {
            hr = ReadDWORD(psi, c_szPropCrawlLevels, (DWORD *)&(pBuf->m_RecurseLevels));
            if (FAILED(hr))
            {
                pBuf->m_RecurseLevels = 0;
            }
        }

        if (pBuf->dwFlags & PROP_WEBCRAWL_FLAGS)
        {
            hr = ReadDWORD(psi, c_szPropCrawlFlags, (DWORD *)&(pBuf->m_RecurseFlags));
            if (FAILED(hr))
            {
                pBuf->m_RecurseFlags = 0;   //  Minimal memory usage.
            }
        }

        if (pBuf->dwFlags & PROP_WEBCRAWL_SIZE)
        {
            hr = ReadDWORD(psi, c_szPropCrawlMaxSize, (DWORD *)&(pBuf->m_SizeLimit));
            if (FAILED(hr))
            {
                pBuf->m_SizeLimit = 0;
            }
        }

        if (pBuf->dwFlags & PROP_WEBCRAWL_ACTUALSIZE)
        {
            hr = ReadDWORD(psi, c_szPropCrawlActualSize, (DWORD *)&(pBuf->m_ActualSize));
            if (FAILED(hr))
            {
                pBuf->m_ActualSize = 0;
            }
        }

        SUBSCRIPTIONITEMINFO sii;
        sii.cbSize = sizeof(SUBSCRIPTIONITEMINFO);
        if (SUCCEEDED(psi->GetSubscriptionItemInfo(&sii)))
        {
            pBuf->grfTaskTrigger = sii.dwFlags;
        }

        psi->Release();
    }

    // FEATURE: Need to support c_szPropEnableShortcutGleam here.

    return S_OK;
}

// If url is NULL, no need to compare.
HRESULT LoadWithCookie(LPCTSTR pszURL, POOEBuf pBuf, DWORD *pdwBufferSize, SUBSCRIPTIONCOOKIE *pCookie)
{
    HRESULT hr = LoadOOEntryInfo(pBuf, pCookie, pdwBufferSize);

    if (SUCCEEDED(hr) && pszURL)
    {
        if (UrlCompare(pBuf->m_URL, pszURL, TRUE))
        {
            TraceMsg(TF_ALWAYS, "Mismatched cookie/URL in LoadWithCookie");
            hr = E_FAIL;      //  Mismatched cookie!
        }
    }

    return hr;
}

HRESULT ReadCookieFromInetDB(LPCTSTR pszURL, SUBSCRIPTIONCOOKIE *pCookie)
{
    ASSERT(pszURL && pCookie);
    PROPVARIANT propCookie;

    PropVariantInit(&propCookie);

    HRESULT hr = FindURLProps(pszURL, &propCookie);

    if (SUCCEEDED(hr) && (propCookie.vt == VT_LPWSTR))
    {
        hr = CLSIDFromString(propCookie.pwszVal, pCookie);
    }
    PropVariantClear(&propCookie);

    //  If we couldn't find it, use a brute force approach
    if (S_OK != hr)
    {
        CEnumSubscription *pes = new CEnumSubscription;

        if (NULL != pes)
        {
            if (SUCCEEDED(pes->Initialize(0)))
            {
                SUBSCRIPTIONCOOKIE cookie;
                BOOL bFound = FALSE;

                while (!bFound && (S_OK == pes->Next(1, &cookie, NULL)))
                {
                    ISubscriptionItem *psi;

                    if (SUCCEEDED(SubscriptionItemFromCookie(FALSE, &cookie, &psi)))
                    {
                        LPTSTR pszCurURL;

                        if (SUCCEEDED(ReadTSTR(psi, c_szPropURL, &pszCurURL)))
                        {
                            bFound = (StrCmpI(pszCurURL, pszURL) == 0);
                            CoTaskMemFree(pszCurURL);
                        }
                        psi->Release();
                    }
                }

                if (bFound)
                {
                    WriteCookieToInetDB(pszURL, &cookie, FALSE);
                    *pCookie = cookie;
                    hr = S_OK;
                }
            }
            pes->Release();
        }
    }

    return hr;
}

HRESULT LoadSubscription(LPCTSTR url, LPMYPIDL *ppidl)
{
    HRESULT hr;

    POOEntry pooe = NULL;
    OOEBuf  ooeBuf;
    DWORD   dwBufferSize;
    SUBSCRIPTIONCOOKIE cookie;

    hr = ReadCookieFromInetDB(url, &cookie);
    if (S_OK == hr)
    {
        hr = LoadWithCookie(url, &ooeBuf, &dwBufferSize, &cookie);
        if (hr == S_OK)
        {
            *ppidl = COfflineFolderEnum::NewPidl(dwBufferSize);
            if (!(*ppidl))
            {
                return E_OUTOFMEMORY;
            }
            pooe = &((*ppidl)->ooe);
            CopyToMyPooe(&ooeBuf, pooe);
        }
        else
        {
            WriteCookieToInetDB(url, NULL, TRUE);
            hr = E_FAIL;
        }
    }
    else
    {
        hr = E_FAIL;
    }

    return hr;
}

// KENSY: This needs to work like GetDefaultInfo

HRESULT GetDefaultOOEBuf(OOEBuf * pBuf, SUBSCRIPTIONTYPE subType)
{
    ASSERT(pBuf);
    ASSERT(IS_VALID_SUBSCRIPTIONTYPE(subType));

    memset((void *)pBuf, 0, sizeof(OOEBuf));
    pBuf->dwFlags = PROP_WEBCRAWL_ALL;
    pBuf->m_RecurseLevels = DEFAULTLEVEL;
    pBuf->m_RecurseFlags = DEFAULTFLAGS;
    pBuf->m_Priority = AGENT_PRIORITY_NORMAL;
    if (subType == SUBSTYPE_CHANNEL || subType == SUBSTYPE_DESKTOPCHANNEL)
    {
        pBuf->clsidDest = CLSID_ChannelAgent;
        pBuf->fChannelFlags = CHANNEL_AGENT_PRECACHE_ALL | CHANNEL_AGENT_DYNAMIC_SCHEDULE;
    }
    else
    {
        pBuf->clsidDest = CLSID_WebCrawlerAgent;
    }
    pBuf->bDesktop = (subType == SUBSTYPE_DESKTOPCHANNEL || subType == SUBSTYPE_DESKTOPURL);
    pBuf->bChannel = (subType == SUBSTYPE_CHANNEL || subType == SUBSTYPE_DESKTOPCHANNEL);
    pBuf->bGleam = !(pBuf->bDesktop);
    pBuf->m_LastUpdated = 0;
    pBuf->m_NextUpdate = 0;

    //  APPCOMPAT: Is this what we want?  IE 4 was DAILY.
    //  Default to not changing the schedule settings -- if it's already subscribed
    //  we won't blast anything and if it's not already subscribed then it will
    //  just be manual.
    pBuf->groupCookie = NOOP_SCHEDULE_COOKIE;

    pBuf->grfTaskTrigger = TASK_FLAG_RUN_IF_CONNECTED_TO_INTERNET |     // Default to not autodial
                           TASK_FLAG_START_ONLY_IF_IDLE;               // and to idle time

    return S_OK;
}

HRESULT CreateSubscriptionFromOOEBuf(OOEBuf *pBuf, LPMYPIDL *ppidl)
{
    HRESULT hr;
    DWORD dwBufferSize = BufferSize(pBuf);
    SUBSCRIPTIONCOOKIE cookie;

    *ppidl = COfflineFolderEnum::NewPidl(dwBufferSize);
    if (!(*ppidl))
    {
        return E_OUTOFMEMORY;
    }
    POOEntry pooe = &((*ppidl)->ooe);
    CopyToMyPooe(pBuf, pooe);

    //  See if the caller has already given us a cookie
    if (GUID_NULL == pooe->m_Cookie)
    {
        //  Nope, see if we have one already
	cookie = pooe->m_Cookie;
        ReadCookieFromInetDB(URL(pooe), &cookie);
	pooe->m_Cookie = cookie;

        if (GUID_NULL == pooe->m_Cookie)
        {
            //  Nope, so create one
            CreateCookie(&pooe->m_Cookie);
        }
    }

    cookie = pooe->m_Cookie;
    WriteCookieToInetDB(URL(pooe), &cookie, FALSE);
    pooe->m_Cookie = cookie;

    WCHAR wszURL[INTERNET_MAX_URL_LENGTH];
    SUBSCRIPTIONITEMINFO sii;
    sii.cbSize = sizeof(SUBSCRIPTIONITEMINFO);
    sii.dwFlags = 0;
    sii.dwPriority = 0;
    sii.ScheduleGroup = CLSID_NULL;
    sii.clsidAgent = pooe->clsidDest;
    MyStrToOleStrN(wszURL, ARRAYSIZE(wszURL), URL(pooe));
    hr = AddUpdateSubscription(&(pooe->m_Cookie), &sii, wszURL, 0, NULL, NULL);

    if (SUCCEEDED(hr))
    {
        hr = WriteProperties(pooe);

        if (SUCCEEDED(hr))
        {
            ISubscriptionItem *psi;

            hr = SubscriptionItemFromCookie(TRUE, &pooe->m_Cookie, &psi);

            if (SUCCEEDED(hr))
            {
                SUBSCRIPTIONSCHEDULE subGroup = GetGroup(pooe);
                SUBSCRIPTIONTYPE   subType = GetItemCategory(pooe);

                if (subGroup == SUBSSCHED_AUTO)
                {
                    if (subType != SUBSTYPE_CHANNEL && subType != SUBSTYPE_DESKTOPCHANNEL)
                    {
                        hr = AddIt(psi, pooe, SUBSSCHED_DAILY);
                    }
                    else
                    {
                        if (pooe->m_Trigger.cbTriggerSize == sizeof(TASK_TRIGGER))
                        {
			    TASK_TRIGGER trigger;

			    trigger = pooe->m_Trigger;
                            hr = ScheduleIt(psi, NAME(pooe), &trigger);
			    pooe->m_Trigger = trigger;

                            pooe->groupCookie = CLSID_NULL;
                        }
                        else
                        {
                            hr = AddIt(psi, pooe, SUBSSCHED_DAILY);
                        }
                    }
                }
                else
                {
                    hr = AddIt(psi, pooe, subGroup);
                }

                psi->Release();
            }
        }
    }

    if (SUCCEEDED(hr))
    {
        FireSubscriptionEvent(SUBSNOTF_CREATE, &pooe->m_Cookie);
    }
    else
    {
        TraceMsg(TF_ALWAYS, "Failed to add new subscription");
        TraceMsg(TF_ALWAYS, "\thr = 0x%x", hr);
        COfflineFolderEnum::FreePidl(*ppidl);
        *ppidl = NULL;
    }

    return hr;
}

HRESULT SendUpdateRequests(HWND hwnd, CLSID * arrClsid, UINT count)
{
    ISubscriptionMgr2 *pSubsMgr2;
    HRESULT hr;

    hr = CoInitialize(NULL);

    if (SUCCEEDED(hr))
    {
        hr = CoCreateInstance(CLSID_SubscriptionMgr, NULL, CLSCTX_INPROC_SERVER,
                              IID_ISubscriptionMgr2, (void**)&pSubsMgr2);
        if (SUCCEEDED(hr))
        {
            SUBSCRIPTIONCOOKIE *pCookies = NULL;
            ULONG nItemsToRun = count;

            if (NULL == arrClsid)
            {
                IEnumSubscription *pes;

                hr = pSubsMgr2->EnumSubscriptions(0, &pes);

                if (SUCCEEDED(hr))
                {
                    ASSERT(NULL != pes);

                    pes->GetCount(&nItemsToRun);
                    if (nItemsToRun > 0)
                    {
                        pCookies = new SUBSCRIPTIONCOOKIE[nItemsToRun];

                        if (NULL != pCookies)
                        {
                            hr = pes->Next(nItemsToRun, pCookies, &nItemsToRun);
                        }
                        else
                        {
                            hr = E_OUTOFMEMORY;
                        }
                    }

                    pes->Release();
                }
            }
            else
            {
                pCookies = arrClsid;
            }

            if (SUCCEEDED(hr))
            {
                hr = pSubsMgr2->UpdateItems(0, nItemsToRun, pCookies);
            }

            if ((NULL == arrClsid) && (NULL != pCookies))
            {
                delete [] pCookies;
            }

            pSubsMgr2->Release();
        }
        CoUninitialize();
    }

    return hr;
}

HRESULT DoDeleteSubscription(POOEntry pooe)
{
    HRESULT hr;
    ISubscriptionItem *psi;

    ASSERT(NULL != pooe);

    hr = SubscriptionItemFromCookie(FALSE, &pooe->m_Cookie, &psi);

    if (SUCCEEDED(hr))
    {
        WritePassword(psi, NULL);

        hr = DoDeleteSubscriptionItem(&pooe->m_Cookie, TRUE);

        if (SUCCEEDED(hr) && (GetItemCategory(pooe) != SUBSTYPE_EXTERNAL))
        {
            WriteCookieToInetDB(URL(pooe), NULL, TRUE);
        }
    }

    return hr;
}

HRESULT PersistUpdate(POOEntry pooe, BOOL bCreate)
{
    HRESULT hr;
    ISubscriptionItem *psi;

    hr = SubscriptionItemFromCookie(bCreate, &(pooe->m_Cookie), &psi);

    if (SUCCEEDED(hr))
    {
        SUBSCRIPTIONITEMINFO sii = { sizeof(SUBSCRIPTIONITEMINFO) };

        hr = psi->GetSubscriptionItemInfo(&sii);

        if (SUCCEEDED(hr) || bCreate)
        {
            sii.clsidAgent = pooe->clsidDest;
            hr = psi->SetSubscriptionItemInfo(&sii);

            if (SUCCEEDED(hr))
            {
                hr = WritePropertiesToItem(pooe, psi);

                if (SUCCEEDED(hr) && IsNativeAgent(pooe->clsidDest))
                {
		    SUBSCRIPTIONCOOKIE cookie;

		    cookie = pooe->m_Cookie;
                    WriteCookieToInetDB(URL(pooe), &cookie, FALSE);
		    pooe->m_Cookie = cookie;
                }
            }
        }

        // REVIEW: should we delete on failure here?
        psi->Release();
    }

    return hr;
}

#ifdef NEWSCHED_AUTONAME
void NewSched_AutoNameHelper(HWND hDlg)
{
    TCHAR szDays[16];
    TCHAR szTime[128];
    TCHAR szFormat[MAX_PATH];
    TCHAR szSchedName[MAX_PATH];
    LPTSTR lpArguments[2];
    BOOL bTranslate;
    int nDays = GetDlgItemInt(hDlg, IDC_SCHEDULE_DAYS, &bTranslate, FALSE);

    if (MLLoadString((nDays == 1) ? IDS_SCHED_FORMAT_DAILY : IDS_SCHED_FORMAT,
        szFormat, ARRAYSIZE(szFormat)))
    {
        TCHAR szTimeFormat[32];
        SYSTEMTIME st;

        DateTime_GetSystemtime(GetDlgItem(hDlg, IDC_SCHEDULE_TIME), &st);

        UpdateTimeFormat(szTimeFormat, ARRAYSIZE(szTimeFormat));
        GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st,
                      szTimeFormat, szTime, ARRAYSIZE(szTime));
        GetDlgItemText(hDlg, IDC_SCHEDULE_DAYS, szDays, ARRAYSIZE(szDays));

        lpArguments[0] = szDays;
        lpArguments[1] = szTime;

        if (FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
                          szFormat, 0, 0, szSchedName, ARRAYSIZE(szSchedName),
                          (va_list *)&lpArguments[0]))
        {
            SetDlgItemText(hDlg, IDC_SCHEDULE_NAME, szSchedName);
        }
    }
}
#endif

BOOL NewSched_ResolveNameConflictHelper(HWND hDlg, TASK_TRIGGER *pTrig,
                                        SYNCSCHEDULECOOKIE *pSchedCookie)
{
    BOOL bResult;

    SYSTEMTIME st;
    TCHAR szSchedName[MAX_PATH];

    GetDlgItemText(hDlg, IDC_SCHEDULE_NAME, szSchedName, ARRAYSIZE(szSchedName));

    TrimWhiteSpace(szSchedName);

    if (szSchedName[0] != 0)
    {
        bResult = TRUE;

        memset(pTrig, 0, sizeof(TASK_TRIGGER));
        pTrig->cbTriggerSize = sizeof(TASK_TRIGGER);

        GetLocalTime(&st);
        pTrig->wBeginYear = st.wYear;
        pTrig->wBeginMonth = st.wMonth;
        pTrig->wBeginDay = st.wDay;

        DateTime_GetSystemtime(GetDlgItem(hDlg, IDC_SCHEDULE_TIME), &st);
        pTrig->wStartHour = st.wHour;
        pTrig->wStartMinute = st.wMinute;

        pTrig->TriggerType = TASK_TIME_TRIGGER_DAILY;

        BOOL bTranslated;
        pTrig->Type.Daily.DaysInterval = (WORD)GetDlgItemInt(hDlg, IDC_SCHEDULE_DAYS, &bTranslated, FALSE);

        int iConflictResult = HandleScheduleNameConflict(szSchedName,
                                                         pTrig,
                                                         hDlg,
                                                         pSchedCookie);
        switch (iConflictResult)
        {
            case CONFLICT_NONE:
                ASSERT(GUID_NULL == *pSchedCookie);
                break;

            case CONFLICT_RESOLVED_USE_NEW:
                ASSERT(GUID_NULL != *pSchedCookie);
                break;

            case CONFLICT_RESOLVED_USE_OLD:
                ASSERT(GUID_NULL == *pSchedCookie);
                pTrig->cbTriggerSize = 0;
                break;

            case CONFLICT_UNRESOLVED:
                bResult = FALSE;
                break;
        }
    }
    else
    {
        SGMessageBox(hDlg, IDS_EMPTY_SCHEDULE_NAME, MB_OK | MB_ICONWARNING);
        bResult = FALSE;
    }

    return bResult;
}

void NewSched_CreateScheduleHelper(HWND hDlg, TASK_TRIGGER *pTrig,
                                   SYNCSCHEDULECOOKIE *pSchedCookie)
{
    HRESULT hr;

    if (GUID_NULL == *pSchedCookie)
    {
        //  Create new schedule
        TCHAR szSchedName[MAX_PATH];
        WCHAR wszSchedName[MAX_PATH];

        DWORD dwSyncScheduleFlags =
            (IsDlgButtonChecked(hDlg, IDC_WIZ_SCHEDULE_AUTOCONNECT) == BST_CHECKED)
                 ? SYNCSCHEDINFO_FLAGS_AUTOCONNECT : 0;

        GetDlgItemText(hDlg, IDC_SCHEDULE_NAME, szSchedName, ARRAYSIZE(szSchedName));
        MyStrToOleStrN(wszSchedName, ARRAYSIZE(wszSchedName), szSchedName);
        hr = CreateSchedule(wszSchedName, dwSyncScheduleFlags, pSchedCookie, pTrig, FALSE);

        ASSERT(SUCCEEDED(hr));
    }
    else if (sizeof(TASK_TRIGGER) == pTrig->cbTriggerSize)
    {
        //  Update existing schedule with new task trigger
        hr = UpdateScheduleTrigger(pSchedCookie, pTrig);
        ASSERT(SUCCEEDED(hr));
    }
    else
    {
        //  Use existing schedule without munging it
    }
}

void NewSched_SetDefaultScheduleName(HWND hDlg)
{
    if (SUCCEEDED(CoInitialize(NULL)))
    {
        ISyncScheduleMgr *pSyncScheduleMgr;

        if (SUCCEEDED(CoCreateInstance(CLSID_SyncMgr, NULL, CLSCTX_ALL,
                                       IID_ISyncScheduleMgr,
                                       (void **)&pSyncScheduleMgr)))
        {
            SYNCSCHEDULECOOKIE schedCookie;
            ISyncSchedule *pSyncSchedule;

            if (SUCCEEDED(pSyncScheduleMgr->CreateSchedule(L"", 0, &schedCookie, &pSyncSchedule)))
            {
                WCHAR wszSchedName[MAX_PATH];
                DWORD cchSchedName = ARRAYSIZE(wszSchedName);

                if (SUCCEEDED(pSyncSchedule->GetScheduleName(&cchSchedName, wszSchedName)))
                {
                    TCHAR szSchedName[MAX_PATH];

                    MyOleStrToStrN(szSchedName, ARRAYSIZE(szSchedName), wszSchedName);
                    SetDlgItemText(hDlg, IDC_SCHEDULE_NAME, szSchedName);
                }
                pSyncSchedule->Release();
            }
            pSyncScheduleMgr->Release();
        }
        CoUninitialize();
    }
}

void NewSched_OnInitDialogHelper(HWND hDlg)
{
    SYSTEMTIME st;

    GetLocalTime(&st);

    Edit_LimitText(GetDlgItem(hDlg, IDC_SCHEDULE_NAME), MAX_PATH - 1);
    Edit_LimitText(GetDlgItem(hDlg, IDC_SCHEDULE_DAYS), 2);
    SendMessage(GetDlgItem(hDlg, IDC_SCHEDULE_DAYS_SPIN),
                UDM_SETRANGE, 0, MAKELONG(99, 1));
    SendMessage(GetDlgItem(hDlg, IDC_SCHEDULE_DAYS_SPIN), UDM_SETPOS, 0, 1);

    HWND hwndTimePicker = GetDlgItem(hDlg, IDC_SCHEDULE_TIME);
    TCHAR szTimeFormat[32];

    UpdateTimeFormat(szTimeFormat, ARRAYSIZE(szTimeFormat));
    DateTime_SetSystemtime(hwndTimePicker, GDT_VALID, &st);
    DateTime_SetFormat(hwndTimePicker, szTimeFormat);

    NewSched_SetDefaultScheduleName(hDlg);
}

int KeepSpinNumberInRange(HWND hdlg, int idEdit, int idSpin, int minVal, int maxVal)
{
    BOOL bTranslate;
    int val = GetDlgItemInt(hdlg, idEdit, &bTranslate, FALSE);
    if (!bTranslate || (val  < minVal) || (val > maxVal))
    {
        //  We have a problem, query the spin control
        val = LOWORD(SendDlgItemMessage(hdlg, idSpin, UDM_GETPOS, 0, 0));
        val = max(minVal, min(maxVal, val));
        SetDlgItemInt(hdlg, idEdit, val, FALSE);
    }

    return val;
}


void SetPropSheetFlags(POOEBuf pBuf, BOOL bSet, DWORD dwPropSheetFlags)
{
    if (bSet)
    {
        pBuf->m_dwPropSheetFlags |= dwPropSheetFlags;
    }
    else
    {
        pBuf->m_dwPropSheetFlags &= ~dwPropSheetFlags;
    }
}


HRESULT FindURLProps(LPCTSTR m_URL, PROPVARIANT * pVarInfo)
{
    HRESULT hr;

    hr = IntSiteHelper(m_URL, &c_rgPropRead[PROP_SUBSCRIPTION], pVarInfo, 1, FALSE);
    return hr;
}

HRESULT LoadOOEntryInfo(POOEBuf pBuf, SUBSCRIPTIONCOOKIE *pCookie, DWORD *pdwSize)
{
    HRESULT hr;

    if (!pBuf || !pCookie || !pdwSize)
    {
        TraceMsg(TF_ALWAYS, "Invalid ARG (1/2/3) %x %x", pBuf, pCookie, pdwSize);
        return E_INVALIDARG;
    }

    ISubscriptionItem *psi;
    hr = SubscriptionItemFromCookie(FALSE, pCookie, &psi);

    if (SUCCEEDED(hr))
    {
        SUBSCRIPTIONITEMINFO sii;

        sii.cbSize = sizeof(SUBSCRIPTIONITEMINFO);

        hr = psi->GetSubscriptionItemInfo(&sii);

        if (SUCCEEDED(hr))
        {
            ZeroMemory((void *)pBuf, sizeof(OOEBuf));
            pBuf->m_Cookie = *pCookie;

        //  TODO: resolve scheduling goo!

        //    pBuf->groupCookie = pItem->groupCookie;
        //    pBuf->grfTaskTrigger = pItem->TaskData.dwTaskFlags;
        /*    if (pItem->groupCookie == CLSID_NULL)   {
                pBuf->m_Trigger = pItem->TaskTrigger;
                if (pBuf->m_Trigger.cbTriggerSize != sizeof(TASK_TRIGGER))  {
                    ASSERT(0);
                    return E_INVALIDARG;
                }
            } else  {
                pBuf->m_Trigger.cbTriggerSize = 0;  //  Invalid
            }
        */

            pBuf->clsidDest = sii.clsidAgent;

            if (!IsNativeAgent(sii.clsidAgent))
            {
                pBuf->dwFlags = PROP_WEBCRAWL_EXTERNAL;
            }
            else
            {
                pBuf->dwFlags = PROP_WEBCRAWL_ALL;
            }

            hr = ReadProperties(pBuf);
            *pdwSize = BufferSize(pBuf);
        }
        psi->Release();
    }

    return hr;
}

/////////////////////////////////////////////////
//
//  SaveBufferChange
//      newBuf: [in/out]
/////////////////////////////////////////////////

HRESULT SaveBufferChange(POOEBuf newBuf, BOOL bCreate)
{
    HRESULT hr;
    DWORD   dwSize;
    POOEntry pooe;
    LPMYPIDL newPidl;

    ASSERT (newBuf);
    if (newBuf->dwFlags == 0)
        return S_OK;

    dwSize = BufferSize(newBuf);
    newPidl = COfflineFolderEnum::NewPidl(dwSize);
    if (!newPidl)
        return E_OUTOFMEMORY;

    pooe = &(newPidl->ooe);
    CopyToMyPooe(newBuf, pooe);
    newBuf->dwFlags = 0;
    hr = PersistUpdate(pooe, bCreate);
    if (SUCCEEDED(hr))  {
        SUBSCRIPTIONCOOKIE cookie;

        DWORD dwPropSheetFlags = newBuf->m_dwPropSheetFlags; //  Preserve prop sheet flags

	cookie = pooe->m_Cookie;
        hr = LoadWithCookie(URL(pooe), newBuf, &dwSize, &cookie);
	pooe->m_Cookie = cookie;

        newBuf->m_dwPropSheetFlags = dwPropSheetFlags;  //  restore
        newBuf->dwFlags = 0;
        if (hr == S_OK)  {
            COfflineFolderEnum::FreePidl(newPidl);
            newPidl = COfflineFolderEnum::NewPidl(dwSize);
            if (!(newPidl))  {
                return E_OUTOFMEMORY;
            }
            pooe = &(newPidl->ooe);
            CopyToMyPooe(newBuf, pooe);
        }
        _GenerateEvent(SHCNE_UPDATEITEM, (LPITEMIDLIST)newPidl, NULL);
    }
    COfflineFolderEnum::FreePidl(newPidl);
    return hr;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

//
// IntSiteHelper
//
// pszURL               url to read/write props for
// pPropspec            properties to read or write
// pReadPropvar         where to store or get properties
// uPropVarArraySize    number of properties
// fWrite               read/write flag
//

HRESULT IntSiteHelper(LPCTSTR pszURL, const PROPSPEC *pPropspec,
        PROPVARIANT *pPropvar, UINT uPropVarArraySize, BOOL fWrite)
{
    HRESULT                     hr;
    IUniformResourceLocator *   purl = NULL;
    IPropertySetStorage *       ppropsetstg = NULL; // init to keep compiler happy
    IPropertyStorage *          ppropstg = NULL; // init to keep compiler happy

    hr = SHCoCreateInstance(NULL, &CLSID_InternetShortcut, NULL,
            IID_IUniformResourceLocator, (LPVOID*)&purl);

    if(SUCCEEDED(hr)) {
        hr = purl->SetURL(pszURL, 0);
    }

    if(SUCCEEDED(hr)) {
        hr = purl->QueryInterface(IID_IPropertySetStorage,
                (LPVOID *)&ppropsetstg);
    }

    if(SUCCEEDED(hr)) {
        hr = ppropsetstg->Open(FMTID_InternetSite, STGM_READWRITE, &ppropstg);
        ppropsetstg->Release();
    }

    if(SUCCEEDED(hr)) {
        if(fWrite) {
            hr = ppropstg->WriteMultiple(uPropVarArraySize, pPropspec,
                            pPropvar, 0);
            ppropstg->Commit(STGC_DEFAULT);
        } else {
            hr = ppropstg->ReadMultiple(uPropVarArraySize, pPropspec,
                            pPropvar);
        }
        ppropstg->Release();
    }

    if(purl)
        purl->Release();

    return hr;
}

// CODE FROM SYNCMGR SOURCES.

//
// Local constants
//
// DEFAULT_TIME_FORMAT - what to use if there's a problem getting format
//                       from system.
//
#define ARRAYLEN(a) (sizeof(a) / sizeof((a)[0]))
#define DEFAULT_TIME_FORMAT         TEXT("hh:mm tt")
#define GET_LOCALE_INFO(lcid)                           \
        {                                               \
            cch = GetLocaleInfo(LOCALE_USER_DEFAULT,    \
                                (lcid),                 \
                                tszScratch,             \
                                ARRAYLEN(tszScratch));  \
            if (!cch)                                   \
            {                                           \
                break;                                  \
            }                                           \
        }
//+--------------------------------------------------------------------------
//
//  Function:   UpdateTimeFormat
//
//  Synopsis:   Construct a time format containing hour and minute for use
//              with the date picker control.
//
//  Arguments:  [tszTimeFormat] - buffer to fill with time format
//              [cchTimeFormat] - size in chars of buffer
//
//  Modifies:   *[tszTimeFormat]
//
//  History:    11-18-1996   DavidMun   Created
//
//  Notes:      This is called on initialization and for wininichange
//              processing.
//
//---------------------------------------------------------------------------
void
UpdateTimeFormat(
        LPTSTR tszTimeFormat,
        ULONG  cchTimeFormat)
{
    ULONG cch;
    TCHAR tszScratch[80];
    BOOL  fAmPm = FALSE;
    BOOL  fAmPmPrefixes = FALSE;
    BOOL  fLeadingZero = FALSE;

    do
    {
        GET_LOCALE_INFO(LOCALE_ITIME);
        fAmPm = (*tszScratch == TEXT('0'));

        if (fAmPm)
        {
            GET_LOCALE_INFO(LOCALE_ITIMEMARKPOSN);
            fAmPmPrefixes = (*tszScratch == TEXT('1'));
        }

        GET_LOCALE_INFO(LOCALE_ITLZERO);
        fLeadingZero = (*tszScratch == TEXT('1'));

        GET_LOCALE_INFO(LOCALE_STIME);

        //
        // See if there's enough room in destination string
        //

        cch = 1                     +  // terminating nul
              1                     +  // first hour digit specifier "h"
              2                     +  // minutes specifier "mm"
              (fLeadingZero != 0)   +  // leading hour digit specifier "h"
              lstrlen(tszScratch)   +  // separator string
              (fAmPm ? 3 : 0);         // space and "tt" for AM/PM

        if (cch > cchTimeFormat)
        {
            cch = 0; // signal error
        }
    } while (0);

    //
    // If there was a problem in getting locale info for building time string
    // just use the default and bail.
    //

    if (!cch)
    {
        StrCpy(tszTimeFormat, DEFAULT_TIME_FORMAT);
        return;
    }

    //
    // Build a time string that has hours and minutes but no seconds.
    //

    tszTimeFormat[0] = TEXT('\0');

    if (fAmPm)
    {
        if (fAmPmPrefixes)
        {
            StrCpy(tszTimeFormat, TEXT("tt "));
        }

        StrCat(tszTimeFormat, TEXT("h"));

        if (fLeadingZero)
        {
            StrCat(tszTimeFormat, TEXT("h"));
        }
    }
    else
    {
        StrCat(tszTimeFormat, TEXT("H"));

        if (fLeadingZero)
        {
            StrCat(tszTimeFormat, TEXT("H"));
        }
    }

    StrCat(tszTimeFormat, tszScratch); // separator
    StrCat(tszTimeFormat, TEXT("mm"));

    if (fAmPm && !fAmPmPrefixes)
    {
        StrCat(tszTimeFormat, TEXT(" tt"));
    }
}
