home *** CD-ROM | disk | FTP | other *** search
/ Power GUI Programming with VisualAge C++ / powergui.iso / trialva / ibmcppw / sdk / mapi / win16 / dev / smh / smh.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-07-11  |  66.3 KB  |  2,338 lines

  1. /*
  2.  *  S M H . C
  3.  *
  4.  *  Sample mail handling hook
  5.  *
  6.  *  Purpose:
  7.  *
  8.  *      The sample mail handling (SMH) hook illustrates the use of the
  9.  *      MAPI Spooler Hook Provider Interface (ISpoolerHook) and those
  10.  *      parts of the general MAPI API that are available to a spooler
  11.  *      hook implementation.
  12.  *
  13.  *      Specifically, SMH illusttrates the operation of both an inbound
  14.  *      message hook as well as outbound.  SMH also has examples of the
  15.  *      configuration of a spooler hook provider via calls to SMH's
  16.  *      ServiceEntry() and/or through calls from the Profile Wizard.
  17.  *
  18.  *  Features:
  19.  *
  20.  *    Sent Mail:
  21.  *
  22.  *      SMH allows the archiving of outbound messages (sent mail), into a
  23.  *      well defined set of subfolders in the default stores sent mail
  24.  *      folder.  The archive folders are comprised of monthly archive
  25.  *      folders.  The monthly folders can be, optionally, created under a
  26.  *      year based folder in the sent mail folder.  Thus in a typical
  27.  *      message store, a fully archived sent mail folder might have a
  28.  *      hierarchy that looks similar to the following:
  29.  *
  30.  *          Sent Mail
  31.  *              |
  32.  *              |-- 1994
  33.  *              |    |
  34.  *              |    |-- 10 October
  35.  *              |    |-- 11 November
  36.  *              |    |-- 12 December
  37.  *              |
  38.  *              |-- 1995
  39.  *                   |
  40.  *                   |-- 01 January
  41.  *
  42.  *      This allows for a mail user to organize their outgoing mail in
  43.  *      a managible fashion.
  44.  *
  45.  *    Deleted Mail:
  46.  *
  47.  *      SMH allows the archiving of deleted mail in the same fashion as
  48.  *      sent mail can be archived.  This feature helps people who choose
  49.  *      keep their 'deleted' mail accessible.  It should be noted here
  50.  *      that this feature does not make use of the ISpoolerHook
  51.  *      interface, but is an example of what all can be done with spooler
  52.  *      hook providers.
  53.  *
  54.  *    Incoming Mail:
  55.  *
  56.  *      SMH can also 'filter' incoming messages and route the message
  57.  *      directly to folders, other than the default stores inbox, based
  58.  *      on message content.  The user can define any number of filters
  59.  *      that can help organize and manage the email.
  60.  *
  61.  *    Unread Search Folders:
  62.  *
  63.  *      Because SMH can filter unread messages deep into a message store
  64.  *      folder hierarchy, SMH can also create a search folder in the root
  65.  *      of the IPM_SUBTREE that searches the entire subtree for unread
  66.  *      messages.
  67.  *
  68.  *  Copyright 1992-95 Microsoft Corporation.  All Rights Reserved.
  69.  */
  70.  
  71. #include "_pch.h"
  72.  
  73. /*
  74.  *  FIsLeapYear()
  75.  *
  76.  *  Used to calculate leap years when determining month ranges for
  77.  *  archive folders.
  78.  */
  79. #define FIsLeapYear(_yr) ((!((_yr) % 400) || ((_yr) % 100) && !((_yr) % 4)) ? TRUE : FALSE)
  80.  
  81. /*
  82.  *  sptMsgDates
  83.  *
  84.  *  The list of properties that are used for archiving.  If
  85.  *  PR_MESSAGE_DELIVERY_TIME is not available, then SMH will use
  86.  *  PR_CLIENT_SUBMIT_TIME.
  87.  */
  88. const static SizedSPropTagArray(2, sptMsgDates) =
  89. {
  90.     2,
  91.     {
  92.         PR_MESSAGE_DELIVERY_TIME,
  93.         PR_CLIENT_SUBMIT_TIME
  94.     }
  95. };
  96.  
  97. /*
  98.  *  sptFldrDates
  99.  *
  100.  *  Theses properties describe the range of dates that an archive folder
  101.  *  with the returned EntryID will support.  This is used in the call to
  102.  *  find a supporting sub-folder.
  103.  */
  104. const static SizedSPropTagArray (3, sptFldrDates) =
  105. {
  106.     3,
  107.     {
  108.         PR_START_DATE,
  109.         PR_END_DATE,
  110.         PR_ENTRYID
  111.     }
  112. };
  113.  
  114. /*
  115.  *  rgtstrMonth
  116.  *  rgtstrMonthFull
  117.  *  rgwDaysPerMonth
  118.  *
  119.  *  These arrays are used in the calculation and creation of supporting
  120.  *  archive folders.
  121.  */
  122. const static TCHAR * rgtstrMonth[] =
  123. {
  124.     TEXT ("Jan"),
  125.     TEXT ("Feb"),
  126.     TEXT ("Mar"),
  127.     TEXT ("Apr"),
  128.     TEXT ("May"),
  129.     TEXT ("Jun"),
  130.     TEXT ("Jul"),
  131.     TEXT ("Aug"),
  132.     TEXT ("Sep"),
  133.     TEXT ("Oct"),
  134.     TEXT ("Nov"),
  135.     TEXT ("Dec")
  136. };
  137. const static TCHAR * rgtstrMonthFull[] =
  138. {
  139.     TEXT ("January"),
  140.     TEXT ("February"),
  141.     TEXT ("March"),
  142.     TEXT ("April"),
  143.     TEXT ("May"),
  144.     TEXT ("June"),
  145.     TEXT ("July"),
  146.     TEXT ("August"),
  147.     TEXT ("September"),
  148.     TEXT ("October"),
  149.     TEXT ("November"),
  150.     TEXT ("December")
  151. };
  152. const static WORD rgwDaysPerMonth[] =
  153. {
  154.     31, //  JAN
  155.     28, //  FEB
  156.     31, //  MAR
  157.     30, //  APR
  158.     31, //  MAY
  159.     30, //  JUN
  160.     31, //  JUL
  161.     31, //  AUG
  162.     30, //  SEP
  163.     31, //  OCT
  164.     30, //  NOV
  165.     31  //  DEC
  166. };
  167.  
  168. /*
  169.  *  sptMessageProps
  170.  *
  171.  *  These are the properties that are required to check filters against
  172.  *  messages.
  173.  */
  174. const static SizedSPropTagArray (cpMsgPrps, sptMsgPrps) =
  175. {
  176.     cpMsgPrps,
  177.     {
  178.         PR_MESSAGE_FLAGS,
  179.         PR_SUBJECT,
  180.         PR_SENT_REPRESENTING_NAME,
  181.         PR_SENT_REPRESENTING_EMAIL_ADDRESS
  182.     }
  183. };
  184.  
  185. extern LPTSTR lpszConfigEvt;
  186. extern SPropTagArray sptRule;
  187. extern SPropTagArray sptConfigProps;
  188.  
  189. /*
  190.  *  vtblSMH
  191.  *
  192.  *  This is the SMH object's vtable.  The table and its functions are
  193.  *  defined by MAPI's ISpoolerHook interface.
  194.  */
  195. static const SMH_Vtbl vtblSMH =
  196. {
  197.     SMH_QueryInterface,
  198.     SMH_AddRef,
  199.     SMH_Release,
  200.     SMH_InboundMsgHook,
  201.     SMH_OutboundMsgHook
  202. };
  203.  
  204.  
  205. /*
  206.  *  HrCacheFolder()
  207.  *
  208.  *  Purpose:
  209.  *
  210.  *      Caches the passed in entryid along with a matching folder
  211.  *
  212.  *  Arguments:
  213.  *
  214.  *      lpsmh       pointer to the sentmail handler
  215.  *      cbeid       count of bytes for the entryid to check
  216.  *      lpeid       data for the entryid to check
  217.  *      lpcbeid     points to the cached entryid size
  218.  *      lppeid      points to the cached entryid data
  219.  *      lppfldr     points to the cached mapi folder object
  220.  *      lpfUpdated  points to cache update flag
  221.  *
  222.  *  Returns:
  223.  *
  224.  *      (HRESULT)
  225.  *      lpcbeid     [OUT] size of newly cached entryid
  226.  *      lppeid      [OUT] data of newly cached entryid
  227.  *      lppfldr     [OUT] folder corresponding to cached entryid
  228.  *      lpfUpdated  [OUT] TRUE iff the out folder is not the
  229.  *                          previously cached folder
  230.  */
  231. HRESULT
  232. HrCacheFolder (LPSMH lpsmh,
  233.     ULONG cbeid,
  234.     LPENTRYID lpeid,
  235.     ULONG FAR * lpcbeid,
  236.     LPENTRYID FAR * lppeid,
  237.     LPMAPIFOLDER FAR * lppfldr,
  238.     BOOL far * lpfUpdated)
  239. {
  240.     HRESULT hr;
  241.     ULONG ulType;
  242.     ULONG ulMatch;
  243.     LPMAPIPROP lpmp = NULL;
  244.  
  245.     /* Init the update flag */
  246.  
  247.     *lpfUpdated = FALSE;
  248.  
  249.     /*  Is the topmost sent mail folder the same folder
  250.      *  as the last filtered message?
  251.      */
  252.     hr = lpsmh->lpsess->lpVtbl->CompareEntryIDs (lpsmh->lpsess,
  253.                                         cbeid,
  254.                                         lpeid,
  255.                                         *lpcbeid,
  256.                                         *lppeid,
  257.                                         0,
  258.                                         &ulMatch);
  259.     if (HR_FAILED (hr) || !ulMatch)
  260.     {
  261.         /* Different folder, guess we better toss the cached one */
  262.  
  263.         (*lpsmh->lpfnFree) (*lppeid);
  264.         *lppeid = NULL;
  265.         *lpcbeid = 0;
  266.  
  267.         /* Cache the SentMail */
  268.  
  269.         hr = ResultFromScode ((*lpsmh->lpfnAlloc) (cbeid, lppeid));
  270.         if (HR_FAILED (hr))
  271.             goto ret;
  272.         memcpy (*lppeid, lpeid, (UINT)cbeid);
  273.         *lpcbeid = cbeid;
  274.         *lpfUpdated = TRUE;
  275.     }
  276.     else if (*lppfldr)
  277.         return hrSuccess;
  278.  
  279.     /* Open the new folder */
  280.  
  281.     hr = lpsmh->lpsess->lpVtbl->OpenEntry (lpsmh->lpsess,
  282.                                         cbeid,
  283.                                         lpeid,
  284.                                         NULL,
  285.                                         MAPI_BEST_ACCESS,
  286.                                         &ulType,
  287.                                         (LPUNKNOWN FAR *)&lpmp);
  288.     if (HR_FAILED (hr))
  289.         goto ret;
  290.  
  291.     if (ulType != MAPI_FOLDER)
  292.     {
  293.         hr = ResultFromScode (MAPI_E_UNEXPECTED_TYPE);
  294.         goto ret;
  295.     }
  296.  
  297. ret:
  298.  
  299.     if (HR_FAILED (hr))
  300.     {
  301.         UlRelease (lpmp);
  302.         (*lpsmh->lpfnFree) (*lppeid);
  303.         *lppeid = NULL;
  304.         *lpcbeid = 0;
  305.         lpmp = NULL;
  306.     }
  307.  
  308.     UlRelease (*lppfldr);
  309.     *lppfldr = (LPMAPIFOLDER)lpmp;
  310.  
  311.     DebugTraceResult (HrCacheFolder(), hr);
  312.     return hr;
  313. }
  314.  
  315.  
  316. /*
  317.  *  HrCreateHashedFolder()
  318.  *
  319.  *  Purpose:
  320.  *
  321.  *      Create/Caches the a folder that satisfies the hash value
  322.  *
  323.  *  Arguments:
  324.  *
  325.  *      lpsmh       pointer to the sentmail handler
  326.  *      lpdft       pointer the the hash interval
  327.  *      lpfldrPar   parnet folder
  328.  *      lpszName    name for folder
  329.  *      lpszComment comment for folder
  330.  *      lpcbeid     points to the cached entryid size
  331.  *      lppeid      points to the cached entryid data
  332.  *      lppfldr     points to the cached mapi folder object
  333.  *
  334.  *  Returns:
  335.  *
  336.  *      (HRESULT)
  337.  *      lpcbeid     [OUT] size of newly cached entryid
  338.  *      lppeid      [OUT] data of newly cached entryid
  339.  *      lppfldr     [OUT] folder corresponding to cached entryid
  340.  */
  341. HRESULT
  342. HrCreateHashedFolder (LPSMH lpsmh,
  343.     LPDFT lpdft,
  344.     LPMAPIFOLDER lpfldrPar,
  345.     LPTSTR lpszName,
  346.     LPTSTR lpszComment,
  347.     ULONG FAR * lpcbeid,
  348.     LPENTRYID FAR * lppeid,
  349.     LPMAPIFOLDER FAR * lppfldr)
  350. {
  351.     HRESULT hr;
  352.     LPMAPIFOLDER lpfldr = NULL;
  353.     LPSPropValue lpval = NULL;
  354.     SPropValue rgval[2] = {0};
  355.     ULONG cval;
  356.  
  357.     /* Toss the current cache info */
  358.  
  359.     (*lpsmh->lpfnFree) (*lppeid);
  360.     *lppeid = NULL;
  361.     *lpcbeid = 0;
  362.  
  363.     /* Create the new folder */
  364.  
  365.     hr = lpfldrPar->lpVtbl->CreateFolder (lpfldrPar,
  366.                             FOLDER_GENERIC,
  367.                             lpszName,
  368.                             lpszComment,
  369.                             NULL,
  370.                             OPEN_IF_EXISTS,
  371.                             &lpfldr);
  372.     if (HR_FAILED (hr))
  373.         goto ret;
  374.  
  375.     /* Set the hashing interval properties */
  376.  
  377.     rgval[0].ulPropTag = PR_START_DATE;
  378.     rgval[0].Value.ft = lpdft->ftStart;
  379.     rgval[1].ulPropTag = PR_END_DATE;
  380.     rgval[1].Value.ft = lpdft->ftEnd;
  381.     hr = lpfldr->lpVtbl->SetProps (lpfldr, 2, rgval, NULL);
  382.     if (HR_FAILED (hr))
  383.         goto ret;
  384.  
  385.     /* Cache the folder info */
  386.  
  387.     hr = lpfldr->lpVtbl->GetProps (lpfldr,
  388.                             (LPSPropTagArray)&sptFldrDates,
  389.                             FALSE,
  390.                             &cval,
  391.                             &lpval);
  392.     if (HR_FAILED (hr))
  393.         goto ret;
  394.  
  395.     /* Make sure we have all the info we need */
  396.  
  397.     if ((lpval[0].ulPropTag != PR_START_DATE) ||
  398.         (lpval[1].ulPropTag != PR_END_DATE) ||
  399.         (lpval[2].ulPropTag != PR_ENTRYID))
  400.     {
  401.         hr = ResultFromScode (MAPI_E_BAD_VALUE);
  402.         goto ret;
  403.     }
  404.  
  405.     /* Cache the entryid */
  406.  
  407.     hr = ResultFromScode ((*lpsmh->lpfnAlloc) (lpval[2].Value.bin.cb, lppeid));
  408.     if (HR_FAILED (hr))
  409.         goto ret;
  410.     memcpy (*lppeid, lpval[2].Value.bin.lpb, (UINT)lpval[2].Value.bin.cb);
  411.     *lpcbeid = lpval[2].Value.bin.cb;
  412.  
  413. ret:
  414.  
  415.     (*lpsmh->lpfnFree) (lpval);
  416.     if (HR_FAILED (hr))
  417.     {
  418.         UlRelease (lpfldr);
  419.         lpfldr = NULL;
  420.     }
  421.     UlRelease (*lppfldr);
  422.     *lppfldr = lpfldr;
  423.  
  424.     DebugTraceResult (HrCreateHashedFolder(), hr);
  425.     return hr;
  426. }
  427.  
  428.  
  429. /*
  430.  *  HrCacheHashedFolder()
  431.  *
  432.  *  Purpose:
  433.  *
  434.  *      Caches the folder that matches the hash value (file time)
  435.  *
  436.  *  Arguments:
  437.  *
  438.  *      lpsmh       pointer to the sentmail handler
  439.  *      ft          hash filetime
  440.  *      lpdft       pointer the the hash interval
  441.  *      lpcbeid     points to the cached entryid size
  442.  *      lppeid      points to the cached entryid data
  443.  *      lppfldr     points to the cached mapi folder object
  444.  *      lpfUpdated  points to cache update flag
  445.  *
  446.  *  Returns:
  447.  *
  448.  *      (HRESULT)
  449.  *      lpcbeid     [OUT] size of newly cached entryid
  450.  *      lppeid      [OUT] data of newly cached entryid
  451.  *      lppfldr     [OUT] folder corresponding to cached entryid
  452.  *      lpfUpdated  [OUT] TRUE iff the out folder is not the
  453.  *                          previously cached folder
  454.  */
  455. HRESULT
  456. HrCacheHashedFolder (LPSMH lpsmh,
  457.     FILETIME ft,
  458.     LPDFT lpdft,
  459.     LPMAPIFOLDER lpfldr,
  460.     ULONG FAR * lpcbeid,
  461.     LPENTRYID FAR * lppeid,
  462.     LPMAPIFOLDER FAR * lppfldr,
  463.     BOOL far * lpfUpdated)
  464. {
  465.     HRESULT hr;
  466.     LPMAPIPROP lpmp = NULL;
  467.     LPMAPITABLE lptbl = NULL;
  468.     LPSRow lprw = NULL;
  469.     LPSRowSet lprws = NULL;
  470.     ULONG ulType;
  471.     UINT i;
  472.  
  473.     /*  Check to see if the new hash fits the
  474.      *  the current hashed folder, if the hash
  475.      *  value does not work, find one that does
  476.      */
  477.     if (!*lpfUpdated &&
  478.         (CompareFileTime (&lpdft->ftStart, &ft) != 1) &&
  479.         (CompareFileTime (&lpdft->ftEnd, &ft) != -1))
  480.     {
  481.         /* The hash works, but do we have a folder? */
  482.  
  483.         if (*lppfldr)
  484.             return hrSuccess;
  485.  
  486.         hr = lpsmh->lpsess->lpVtbl->OpenEntry (lpsmh->lpsess,
  487.                                         *lpcbeid,
  488.                                         *lppeid,
  489.                                         NULL,
  490.                                         MAPI_BEST_ACCESS,
  491.                                         &ulType,
  492.                                         (LPUNKNOWN FAR *)&lpmp);
  493.         if (!HR_FAILED (hr) && (ulType != MAPI_FOLDER))
  494.         {
  495.             hr = ResultFromScode (MAPI_E_UNEXPECTED_TYPE);
  496.             UlRelease (lpmp);
  497.             lpmp = NULL;
  498.         }
  499.         goto ret;
  500.     }
  501.  
  502.     /* Toss the cached info */
  503.  
  504.     (*lpsmh->lpfnFree) (*lppeid);
  505.     *lppeid = NULL;
  506.     *lpcbeid = 0;
  507.  
  508.     /* Get the hierachy and set it up to find the target folder */
  509.  
  510.     hr = lpfldr->lpVtbl->GetHierarchyTable (lpfldr, 0, &lptbl);
  511.     if (HR_FAILED (hr))
  512.         goto ret;
  513.  
  514.     hr = lptbl->lpVtbl->SetColumns (lptbl, (LPSPropTagArray)&sptFldrDates, 0);
  515.     if (HR_FAILED (hr))
  516.         goto ret;
  517.  
  518.     hr = lptbl->lpVtbl->QueryRows (lptbl, 12, 0L, &lprws);
  519.     if (HR_FAILED (hr))
  520.         goto ret;
  521.  
  522.     while (lprws->cRows)
  523.     {
  524.         for (i = 0; i < lprws->cRows; i++)
  525.         {
  526.             lprw = &lprws->aRow[i];
  527.  
  528.             if (!lpmp &&
  529.                 (lprw->lpProps[0].ulPropTag == PR_START_DATE) &&
  530.                 (lprw->lpProps[1].ulPropTag == PR_END_DATE) &&
  531.                 (CompareFileTime (&lprw->lpProps[0].Value.ft, &ft) != 1) &&
  532.                 (CompareFileTime (&lprw->lpProps[1].Value.ft, &ft) != -1))
  533.             {
  534.                 /* Hey, this looks like the folder we want */
  535.  
  536.                 hr = HrCacheFolder (lpsmh,
  537.                         lprw->lpProps[2].Value.bin.cb,
  538.                         (LPENTRYID)lprw->lpProps[2].Value.bin.lpb,
  539.                         lpcbeid,
  540.                         lppeid,
  541.                         (LPMAPIFOLDER FAR *)&lpmp,
  542.                         lpfUpdated);
  543.                 if (!HR_FAILED (hr))
  544.                 {
  545.                     lpdft->ftStart = lprw->lpProps[0].Value.ft;
  546.                     lpdft->ftEnd = lprw->lpProps[1].Value.ft;
  547.                 }
  548.             }
  549.         }
  550.  
  551.         /* Clean up the row properies */
  552.  
  553.         for (i = 0; i < lprws->cRows; i++)
  554.             (*lpsmh->lpfnFree) (lprws->aRow[i].lpProps);
  555.  
  556.         /* We either found the folder or we had an error */
  557.  
  558.         if (lpmp || HR_FAILED (hr))
  559.             break;
  560.  
  561.         /* Clean up the row set */
  562.  
  563.         (*lpsmh->lpfnFree) (lprws);
  564.         hr = lptbl->lpVtbl->QueryRows (lptbl, 12, 0L, &lprws);
  565.         if (HR_FAILED (hr))
  566.             goto ret;
  567.     }
  568.  
  569.     /* Clean up the final row set */
  570.  
  571.     (*lpsmh->lpfnFree) (lprws);
  572.  
  573. ret:
  574.  
  575.     UlRelease (lptbl);
  576.     UlRelease (*lppfldr);
  577.     *lppfldr = (LPMAPIFOLDER)lpmp;
  578.  
  579.     DebugTraceResult (HrCacheHashedFolder(), hr);
  580.     return hr ? hr : (lpmp ? hrSuccess : ResultFromScode (MAPI_E_NOT_FOUND));
  581. }
  582.  
  583.  
  584. /*
  585.  *  HrArchiveMessage()
  586.  *
  587.  *  Purpose:
  588.  *
  589.  *      The purpose of this function is to "hash" a single message by
  590.  *      processing based on date.  The most obvious bucket size is
  591.  *      monthly but there is no reason not to make this an option the
  592.  *      user could confiigure.
  593.  *
  594.  *  Arguments:
  595.  *
  596.  *      lpsmh           this filter hook obj
  597.  *      lpmsg           the message to be filtered
  598.  *      lpfldrDef       the owning folder of the message
  599.  *      lpmdbDef        the owning store of the message
  600.  *      lpbkit          the cached bucket structure
  601.  *      fCatByYear      uses yearly subfolders iff TRUE
  602.  *      lpcbeid         cb for entryid of default target for message
  603.  *      lppbeid         pb for entryid of default target for message
  604.  *
  605.  *  Operation:
  606.  *
  607.  *      Opens the suggested folder (if needed) and checks for the
  608.  *      existence of the appropriate "bucket" folder.  If it does exist,
  609.  *      then the  folder is created and cached.  The entryid is grabbed
  610.  *      and passed back in to the spooler.
  611.  *
  612.  *      IMPORTANT: the entryid passed in will be swapped out by this
  613.  *      call.  Therefore the *lppeid buffer must be allocated with the
  614.  *      MAPIAllocateBuffer provider to the filter.
  615.  *
  616.  *  Returns:
  617.  *
  618.  *      (HRESULT)
  619.  *      lpcbeid [out]   the size of the returned EntryID
  620.  *      lppbeid [out]   the data of the returned EntryID
  621.  *
  622.  */
  623. HRESULT
  624. HrArchiveMessage (LPSMH lpsmh,
  625.     LPMESSAGE lpmsg,
  626.     LPMAPIFOLDER lpfldrDef,
  627.     LPMDB lpmdbDef,
  628.     LPBKIT lpbkit,
  629.     BOOL fCatByYear,
  630.     ULONG FAR * lpcbeid,
  631.     LPBYTE FAR * lppeid)
  632. {
  633.     HRESULT hr = hrSuccess;
  634.     BOOL fUpdated = FALSE;
  635.     FILETIME ft;
  636.     LPMAPIFOLDER lpfldr;
  637.     LPSPropValue lpval = NULL;
  638.     SYSTEMTIME st;
  639.     TCHAR rgchName[64] = {0};
  640.     ULONG cval;
  641.  
  642.     /* Quick and dirty parameter check */
  643.  
  644.     if (IsBadWritePtr (lpsmh, sizeof(SMH)) ||
  645.         IsBadWritePtr (lpcbeid, sizeof(ULONG)) ||
  646.         IsBadWritePtr (lppeid, sizeof(LPBYTE)) ||
  647.         IsBadWritePtr (*lppeid, (UINT)(*lpcbeid)))
  648.         return ResultFromScode (MAPI_E_INVALID_PARAMETER);
  649.  
  650.     /* Get the date used by the hash */
  651.  
  652.     hr = lpmsg->lpVtbl->GetProps (lpmsg,
  653.                             (LPSPropTagArray)&sptMsgDates,
  654.                             FALSE,
  655.                             &cval,
  656.                             &lpval);
  657.     if (HR_FAILED (hr))
  658.         goto ret;
  659.  
  660.     /* Make sure what we end up with is usable */
  661.  
  662.     if (lpval[0].ulPropTag == PR_MESSAGE_DELIVERY_TIME)
  663.     {
  664.         DebugTrace ("SMH: filtering on PR_MESSAGE_DELIVERY_TIME\n");
  665.         ft = lpval[0].Value.ft;
  666.     }
  667.     else if (lpval[1].ulPropTag == PR_CLIENT_SUBMIT_TIME)
  668.     {
  669.         DebugTrace ("SMH: filtering on PR_CLIENT_SUBMIT_TIME\n");
  670.         ft = lpval[0].Value.ft;
  671.     }
  672.     else
  673.     {
  674.         DebugTrace ("SMH: cannot filter on provided time props\n");
  675.         hr = ResultFromScode (MAPI_E_BAD_VALUE);
  676.         goto ret;
  677.     }
  678.     if (!FileTimeToSystemTime (&ft, &st))
  679.     {
  680.         hr = ResultFromScode (MAPI_E_BAD_VALUE);
  681.         goto ret;
  682.     }
  683.  
  684.     /* Cache the parent folder */
  685.  
  686.     hr = HrCacheFolder (lpsmh,
  687.                     *lpcbeid,
  688.                     (LPENTRYID)*lppeid,
  689.                     &lpbkit->cbeidParent,
  690.                     &lpbkit->lpeidParent,
  691.                     &lpbkit->lpfldrParent,
  692.                     &fUpdated);
  693.     if (HR_FAILED (hr))
  694.         goto ret;
  695.  
  696.     if (fCatByYear)
  697.     {
  698.         /* Cache the year folder */
  699.  
  700.         hr = HrCacheHashedFolder (lpsmh,
  701.                     ft,
  702.                     &lpbkit->dftYr,
  703.                     lpbkit->lpfldrParent,
  704.                     &lpbkit->cbeidYr,
  705.                     &lpbkit->lpeidYr,
  706.                     &lpbkit->lpfldrYr,
  707.                     &fUpdated);
  708.         if (HR_FAILED (hr) && (GetScode (hr) == MAPI_E_NOT_FOUND))
  709.         {
  710.             wsprintf (rgchName, "%04hu", st.wYear);
  711.             st.wMonth = 1;
  712.             st.wDayOfWeek = 0;
  713.             st.wDay = 1;
  714.             st.wHour = 0;
  715.             st.wMinute = 0;
  716.             st.wSecond = 0;
  717.             st.wMilliseconds = 0;
  718.             if (!SystemTimeToFileTime (&st, &lpbkit->dftYr.ftStart))
  719.             {
  720.                 hr = ResultFromScode (MAPI_E_BAD_VALUE);
  721.                 goto ret;
  722.             }
  723.             st.wDay = rgwDaysPerMonth[st.wMonth - 1];
  724.             st.wMonth = 12;
  725.             st.wDayOfWeek = 0;
  726.             st.wDay = 31;
  727.             st.wHour = 23;
  728.             st.wMinute = 59;
  729.             st.wSecond = 59;
  730.             st.wMilliseconds = 999;
  731.             if (!SystemTimeToFileTime (&st, &lpbkit->dftYr.ftEnd))
  732.             {
  733.                 hr = ResultFromScode (MAPI_E_BAD_VALUE);
  734.                 goto ret;
  735.             }
  736.             hr = HrCreateHashedFolder (lpsmh,
  737.                             &lpbkit->dftYr,
  738.                             lpbkit->lpfldrParent,
  739.                             rgchName,
  740.                             NULL,
  741.                             &lpbkit->cbeidYr,
  742.                             &lpbkit->lpeidYr,
  743.                             &lpbkit->lpfldrYr);
  744.             if (HR_FAILED (hr))
  745.                 goto ret;
  746.         }
  747.         else if (HR_FAILED (hr))
  748.             goto ret;
  749.  
  750.         lpfldr = lpbkit->lpfldrYr;
  751.     }
  752.     else
  753.         lpfldr = lpbkit->lpfldrParent;
  754.  
  755.     /* Cache the hashed target folder */
  756.  
  757.     hr = HrCacheHashedFolder (lpsmh,
  758.                     ft,
  759.                     &lpbkit->dft,
  760.                     lpfldr,
  761.                     &lpbkit->cbeid,
  762.                     &lpbkit->lpeid,
  763.                     &lpbkit->lpfldr,
  764.                     &fUpdated);
  765.     if (HR_FAILED (hr) && (GetScode (hr) == MAPI_E_NOT_FOUND))
  766.     {
  767.         if (!FileTimeToSystemTime (&ft, &st))
  768.         {
  769.             hr = ResultFromScode (MAPI_E_BAD_VALUE);
  770.             goto ret;
  771.         }
  772.         if (fCatByYear)
  773.         {
  774.             wsprintf (rgchName,
  775.                 "%02hu %s",
  776.                 st.wMonth,
  777.                 rgtstrMonthFull[st.wMonth - 1]);
  778.         }
  779.         else
  780.         {
  781.             wsprintf (rgchName,
  782.                 TEXT("'%02hu/%02hu %s"),
  783.                 st.wYear % 100,
  784.                 st.wMonth,
  785.                 rgtstrMonthFull[st.wMonth - 1]);
  786.         }
  787.         st.wDayOfWeek = (st.wDay - st.wDayOfWeek - 1) % 7;
  788.         st.wDay = 1;
  789.         st.wHour = 0;
  790.         st.wMinute = 0;
  791.         st.wSecond = 0;
  792.         st.wMilliseconds = 0;
  793.         if (!SystemTimeToFileTime (&st, &lpbkit->dft.ftStart))
  794.         {
  795.             hr = ResultFromScode (MAPI_E_BAD_VALUE);
  796.             goto ret;
  797.         }
  798.         st.wDay = rgwDaysPerMonth[st.wMonth - 1];
  799.         if ((st.wMonth == 1) && FIsLeapYear (st.wYear))
  800.             st.wDay += 1;
  801.         st.wDayOfWeek = (st.wDayOfWeek + st.wDay - 1) % 7;
  802.         st.wHour = 23;
  803.         st.wMinute = 59;
  804.         st.wSecond = 59;
  805.         st.wMilliseconds = 999;
  806.         if (!SystemTimeToFileTime (&st, &lpbkit->dft.ftEnd))
  807.         {
  808.             hr = ResultFromScode (MAPI_E_BAD_VALUE);
  809.             goto ret;
  810.         }
  811.         hr = HrCreateHashedFolder (lpsmh,
  812.                             &lpbkit->dft,
  813.                             lpfldr,
  814.                             rgchName,
  815.                             NULL,
  816.                             &lpbkit->cbeid,
  817.                             &lpbkit->lpeid,
  818.                             &lpbkit->lpfldr);
  819.     }
  820.  
  821. ret:
  822.  
  823.     if (!HR_FAILED (hr))
  824.     {
  825.         LPBYTE lpeid;
  826.  
  827.         /* OK, If we get this far we are moving the message */
  828.  
  829.         hr = ResultFromScode ((*lpsmh->lpfnAlloc) (lpbkit->cbeid, &lpeid));
  830.         if (HR_FAILED (hr))
  831.             goto ret;
  832.         memcpy (lpeid, lpbkit->lpeid, (UINT)lpbkit->cbeid);
  833.         (*lpsmh->lpfnFree) (*lppeid);
  834.         *lpcbeid = lpbkit->cbeid;
  835.         *lppeid = lpeid;
  836.     }
  837.     (*lpsmh->lpfnFree) (lpval);
  838.     lpval = NULL;
  839.  
  840.     DebugTraceResult (HrArchiveMessage(), hr);
  841.     return hr;
  842. }
  843.  
  844.  
  845. /*
  846.  *  HrFilterDeleted()
  847.  *
  848.  *  Purpose:
  849.  *
  850.  *      Filters all the current message from the 'Wastebasket'/'Deleted Items'
  851.  *      folder based on the archiving model used in sent mail processing.
  852.  *
  853.  *  Arguments:
  854.  *
  855.  *      lpwb            wastbucket struct for the current store
  856.  *      lpbin           sbinary holding the entryid of the message
  857.  *
  858.  *  Returns:
  859.  *
  860.  *      (HRESULT)
  861.  */
  862. HRESULT
  863. HrFilterDeleted (LPWB lpwb, LPSBinary lpbin)
  864. {
  865.     HRESULT hr = ResultFromScode (MAPI_E_NOT_ENOUGH_MEMORY);
  866.     LPBYTE lpeid = NULL;
  867.     LPMAPIFOLDER lpfldr = lpwb->lpfldr;
  868.     LPMDB lpmdb = lpwb->lpmdb;
  869.     LPMESSAGE lpmsg = NULL;
  870.     LPSMH lpsmh = lpwb->lpsmh;
  871.     SBinaryArray sba = {1, lpbin};
  872.     ULONG cb = lpwb->lpvalEid->Value.bin.cb;
  873.     ULONG ulFlags = 0;
  874.     ULONG ulType;
  875.  
  876.     if (!FAILED ((*lpsmh->lpfnAlloc) (cb, &lpeid)))
  877.     {
  878.         hr = lpmdb->lpVtbl->OpenEntry (lpmdb,
  879.                                 lpbin->cb,
  880.                                 (LPENTRYID)lpbin->lpb,
  881.                                 NULL,
  882.                                 0,
  883.                                 &ulType,
  884.                                 (LPUNKNOWN FAR *)&lpmsg);
  885.         if (!HR_FAILED (hr))
  886.         {
  887.             memcpy (lpeid, lpwb->lpvalEid->Value.bin.lpb, (UINT)cb);
  888.             hr = HrArchiveMessage (lpsmh,
  889.                                 lpmsg,
  890.                                 lpfldr,
  891.                                 lpmdb,
  892.                                 &lpwb->bkit,
  893.                                 lpsmh->fCatWb,
  894.                                 &cb,
  895.                                 &lpeid);
  896.             if (!HR_FAILED (hr))
  897.             {
  898.                 hr = lpfldr->lpVtbl->CopyMessages (lpfldr,
  899.                                 &sba,
  900.                                 NULL,
  901.                                 lpwb->bkit.lpfldr,
  902.                                 0,
  903.                                 NULL,
  904.                                 MAPI_MOVE);
  905.             }
  906.         }
  907.     }
  908.     (*lpsmh->lpfnFree) (lpeid);
  909.     DebugTraceResult (HrFilterDeleted(), hr);
  910.     return hr;
  911. }
  912.  
  913.  
  914. /*
  915.  *  WBNotify()
  916.  *
  917.  *  Purpose:
  918.  *
  919.  *      Notification callback on the WB folders of message stores.  When
  920.  *      rows are added to the WB contents table, we enum the table and
  921.  *      filter each message added.
  922.  *
  923.  *  Arguments:
  924.  *
  925.  *      lpv         void pointer to current WB struct
  926.  *      cntf        count of notifications
  927.  *      lpntf       notifications
  928.  *
  929.  *  Returns:
  930.  *
  931.  *      (SCODE)
  932.  */
  933. STDAPI_(SCODE)
  934. WBNotify (LPVOID lpv, ULONG cntf, LPNOTIFICATION lpntf)
  935. {
  936.     HRESULT hr;
  937.     LPMAPITABLE lptbl;
  938.     LPSMH lpsmh;
  939.     LPSRowSet lprws = NULL;
  940.     LPWB lpwb;
  941.     UINT irw;
  942.  
  943.     /* Quick and dirty check on the context */
  944.  
  945.     if (IsBadReadPtr (lpv, sizeof(WB)) ||
  946.         IsBadReadPtr (((LPWB)lpv)->lpsmh, sizeof(SMH)))
  947.         return S_OK;
  948.  
  949.     lpwb = (LPWB)lpv;
  950.     lptbl = lpwb->lptbl;
  951.     lpsmh = lpwb->lpsmh;
  952.  
  953.     /* Just incase we were turned off */
  954.  
  955.     if (lpsmh->fCatWb)
  956.     {
  957.         while (cntf--)
  958.         {
  959.             Assert (lpntf->ulEventType == fnevTableModified);
  960.             if (lpntf->info.tab.ulTableEvent == TABLE_ROW_ADDED)
  961.             {
  962.                 lptbl->lpVtbl->SeekRow (lptbl, BOOKMARK_BEGINNING, 0, NULL);
  963.  
  964.                 while (TRUE)
  965.                 {
  966.                     hr = lptbl->lpVtbl->QueryRows (lptbl, 5, 0, &lprws);
  967.                     if (HR_FAILED (hr))
  968.                         break;
  969.  
  970.                     for (irw = 0; irw < lprws->cRows; irw++)
  971.                     {
  972.                         HrFilterDeleted (lpwb, &lprws->aRow[irw].lpProps[0].Value.bin);
  973.                         (*lpsmh->lpfnFree) (lprws->aRow[irw].lpProps);
  974.                     }
  975.  
  976.                     if (!lprws->cRows)
  977.                         break;
  978.  
  979.                     (*lpsmh->lpfnFree) (lprws);
  980.                     lprws = NULL;
  981.                 }
  982.                 break;
  983.             }
  984.             lpntf++;
  985.         }
  986.         (*lpsmh->lpfnFree) (lprws);
  987.     }
  988.     return S_OK;
  989. }
  990.  
  991.  
  992. /*
  993.  *  HrInitDeletedMailFilter()
  994.  *
  995.  *  Purpose:
  996.  *
  997.  *      Inits the deleted mail filters by opening the store, finding the
  998.  *      WB folder, opening the contents table of the WB, and registering
  999.  *      for TABLE_CHANGED notifications.
  1000.  *
  1001.  *  Arguments:
  1002.  *
  1003.  *      lpsmg           the sample mail handler object
  1004.  *
  1005.  *  Returns:
  1006.  *
  1007.  *      (HRESULT)
  1008.  */
  1009. HRESULT
  1010. HrInitDeletedMailFilter (LPSMH lpsmh)
  1011. {
  1012.     HRESULT hr;
  1013.     LPMAPIADVISESINK lpadvz = NULL;
  1014.     LPMAPIFOLDER lpfldr = NULL;
  1015.     LPMAPITABLE lptbl = NULL;
  1016.     LPMDB lpmdb = NULL;
  1017.     LPSPropValue lpval = NULL;
  1018.     LPWB lpwb = NULL;
  1019.     SizedSPropTagArray (1, spt) = {1, { PR_ENTRYID }};
  1020.     ULONG ulType;
  1021.     UINT cerr = 0;
  1022.     UINT i;
  1023.  
  1024.     for (i = 0; i < lpsmh->lpstotbl->cSto; i++)
  1025.     {
  1026.         hr = ResultFromScode ((*lpsmh->lpfnAlloc) (sizeof(WB), &lpwb));
  1027.         if (HR_FAILED (hr))
  1028.             goto nxt;
  1029.         memset (lpwb, 0, sizeof(WB));
  1030.  
  1031.         hr = HrOpenStoEntry (lpsmh->lpsess, &lpsmh->lpstotbl->aSto[i], &lpmdb);
  1032.         if (HR_FAILED (hr))
  1033.             goto nxt;
  1034.  
  1035.         hr = HrGetOneProp ((LPMAPIPROP)lpmdb, PR_IPM_WASTEBASKET_ENTRYID, &lpval);
  1036.         if (HR_FAILED (hr))
  1037.             goto nxt;
  1038.  
  1039.         hr = lpmdb->lpVtbl->OpenEntry (lpmdb,
  1040.                                 lpval->Value.bin.cb,
  1041.                                 (LPENTRYID)lpval->Value.bin.lpb,
  1042.                                 NULL,
  1043.                                 MAPI_MODIFY,
  1044.                                 &ulType,
  1045.                                 (LPUNKNOWN FAR *)&lpfldr);
  1046.         if (HR_FAILED (hr))
  1047.             goto nxt;
  1048.  
  1049.         hr = lpfldr->lpVtbl->GetContentsTable (lpfldr, 0, &lptbl);
  1050.         if (HR_FAILED (hr))
  1051.             goto nxt;
  1052.  
  1053.         hr = lptbl->lpVtbl->SetColumns (lptbl, (LPSPropTagArray)&spt, 0);
  1054.         if (HR_FAILED (hr))
  1055.             goto nxt;
  1056.  
  1057.         hr = HrAllocAdviseSink ((LPNOTIFCALLBACK)&WBNotify, lpwb, &lpadvz);
  1058.         if (HR_FAILED (hr))
  1059.             goto nxt;
  1060.  
  1061.         hr = lptbl->lpVtbl->Advise (lptbl, fnevTableModified, lpadvz, &lpwb->ulAdvz);
  1062.         if (HR_FAILED (hr))
  1063.             goto nxt;
  1064.  
  1065.         UlAddRef (lptbl);
  1066.         UlAddRef (lpfldr);
  1067.         lpwb->lpmdb = lpmdb;
  1068.         lpwb->lptbl = lptbl;
  1069.         lpwb->lpfldr = lpfldr;
  1070.         lpwb->lpvalEid = lpval;
  1071.         lpwb->lpsmh = lpsmh;
  1072.         lpval = NULL;
  1073.  
  1074.         /* Hook it in */
  1075.  
  1076.         lpwb->wbNext = lpsmh->lstWb;
  1077.         lpsmh->lstWb = lpwb;
  1078.         lpwb = NULL;
  1079. nxt:
  1080.  
  1081.         if (HR_FAILED (hr))
  1082.             cerr++;
  1083.  
  1084.         (*lpsmh->lpfnFree) (lpval);
  1085.         lpval = NULL;
  1086.  
  1087.         (*lpsmh->lpfnFree) (lpwb);
  1088.         lpwb = NULL;
  1089.  
  1090.         UlRelease (lpadvz);
  1091.         lpadvz = NULL;
  1092.  
  1093.         UlRelease (lpfldr);
  1094.         lpfldr = NULL;
  1095.  
  1096.         UlRelease (lptbl);
  1097.         lptbl = NULL;
  1098.     }
  1099.  
  1100.     hr = ResultFromScode (cerr ? MAPI_W_ERRORS_RETURNED : S_OK);
  1101.     DebugTraceResult (HrInitDeletedMailFilter(), hr);
  1102.     return hr;
  1103. }
  1104.  
  1105.  
  1106. /*
  1107.  *  HrInitUnreadSearch()
  1108.  *
  1109.  *  Purpose:
  1110.  *
  1111.  *      Inits/creates an 'Unread Messages' search folder in the root of
  1112.  *      the given store's IPM_SUBTREE hierarchy.
  1113.  *
  1114.  *  Arguments:
  1115.  *
  1116.  *      lpsmh           the sample mail handler object
  1117.  *      lpmdb           the store getting the search folder
  1118.  *
  1119.  *  Returns:
  1120.  *
  1121.  *      (HRESULT)
  1122.  */
  1123. HRESULT
  1124. HrInitUnreadSearch (LPSMH lpsmh)
  1125. {
  1126.     HRESULT hr;
  1127.     ENTRYLIST el = {0};
  1128.     LPMAPIFOLDER lpfldr = NULL;
  1129.     LPMAPIFOLDER lpfldrUM = NULL;
  1130.     LPMDB lpmdb = NULL;
  1131.     LPSPropValue lpval = NULL;
  1132.     SRestriction res = {0};
  1133.     ULONG ulType = 0;
  1134.     UINT cerr = 0;
  1135.     UINT i;
  1136.  
  1137.     for (i = 0; i < lpsmh->lpstotbl->cSto; i++)
  1138.     {
  1139.         hr = HrOpenStoEntry (lpsmh->lpsess, &lpsmh->lpstotbl->aSto[i], &lpmdb);
  1140.         if (!HR_FAILED (hr))
  1141.         {
  1142.             hr = HrGetOneProp ((LPMAPIPROP)lpmdb, PR_IPM_SUBTREE_ENTRYID, &lpval);
  1143.             if (!HR_FAILED (hr))
  1144.             {
  1145.                 hr = lpmdb->lpVtbl->OpenEntry (lpmdb,
  1146.                                         lpval->Value.bin.cb,
  1147.                                         (LPENTRYID)lpval->Value.bin.lpb,
  1148.                                         NULL,
  1149.                                         MAPI_MODIFY,
  1150.                                         &ulType,
  1151.                                         (LPUNKNOWN FAR *)&lpfldr);
  1152.                 if (!HR_FAILED (hr))
  1153.                 {
  1154.                     hr = lpfldr->lpVtbl->CreateFolder (lpfldr,
  1155.                                         FOLDER_SEARCH,
  1156.                                         "Unread Messages",
  1157.                                         "Simple Mail Handler: unread message search",
  1158.                                         NULL,
  1159.                                         MAPI_MODIFY | OPEN_IF_EXISTS,
  1160.                                         &lpfldrUM);
  1161.                     if (!HR_FAILED (hr))
  1162.                     {
  1163.                         el.cValues = 1;
  1164.                         el.lpbin = &lpval->Value.bin;
  1165.                         res.rt = RES_BITMASK;
  1166.                         res.res.resBitMask.relBMR = BMR_EQZ;
  1167.                         res.res.resBitMask.ulPropTag = PR_MESSAGE_FLAGS;
  1168.                         res.res.resBitMask.ulMask = MSGFLAG_READ;
  1169.                         hr = lpfldrUM->lpVtbl->SetSearchCriteria (lpfldrUM,
  1170.                                         &res,
  1171.                                         &el,
  1172.                                         RECURSIVE_SEARCH | BACKGROUND_SEARCH | RESTART_SEARCH);
  1173.                         UlRelease (lpfldrUM);
  1174.                         lpfldrUM = NULL;
  1175.                     }
  1176.                     UlRelease (lpfldr);
  1177.                     lpfldr = NULL;
  1178.                 }
  1179.                 (*lpsmh->lpfnFree) (lpval);
  1180.                 lpval = NULL;
  1181.             }
  1182.         }
  1183.         if (HR_FAILED (hr))
  1184.         {
  1185.             DebugTrace ("SMH: WARNING: failed to init unread search (store:%d)\n", i);
  1186.             cerr++;
  1187.         }
  1188.     }
  1189.  
  1190.     hr = ResultFromScode (cerr ? MAPI_W_ERRORS_RETURNED : S_OK);
  1191.     DebugTraceResult (HrInitUnreadSearch(), hr);
  1192.     return hr;
  1193. }
  1194.  
  1195.  
  1196. /*
  1197.  *  LpszFindChar()
  1198.  *
  1199.  *  Purpose:
  1200.  *
  1201.  *      Finds the given character in the passed in string.  This is an
  1202.  *      exact matching model so strings should be normalized to either
  1203.  *      upper or lower case if case insensitvity is required.
  1204.  *
  1205.  *  Arguments:
  1206.  *
  1207.  *      lpszSrc         source string
  1208.  *      ch              character to find
  1209.  *
  1210.  *  Returns:
  1211.  *
  1212.  *      NULL iff the char was not found, otherwise, the return is a
  1213.  *      pointer to the character in the source string.
  1214.  */
  1215. LPTSTR
  1216. LpszFindChar (LPTSTR lpszSrc, TCHAR ch)
  1217. {
  1218.     LPTSTR lpsz = lpszSrc;
  1219.  
  1220.     if (lpszSrc)
  1221.     {
  1222.         while (*lpsz && (*lpsz != ch))
  1223.             lpsz++;
  1224.     }
  1225.     else
  1226.         return NULL;
  1227.  
  1228.     return (*lpsz ? lpsz : NULL);
  1229. }
  1230.  
  1231.  
  1232. /*
  1233.  *  FLpszContainsLpsz()
  1234.  *
  1235.  *  Purpose:
  1236.  *
  1237.  *      Finds the given sub-string in the passed in string.  This is a
  1238.  *      case insensitive matching model that normalizes the strings to
  1239.  *      upper case.  So, if preservation is required, pass in copies of
  1240.  *      the two strings
  1241.  *
  1242.  *  Arguments:
  1243.  *
  1244.  *      lpszSrc         source string
  1245.  *      lpsz            string to find
  1246.  *
  1247.  *  Returns:
  1248.  *
  1249.  *      FALSE iff the string was not found, TRUE otherwise.
  1250.  */
  1251. BOOL
  1252. FLpszContainsLpsz (LPTSTR lpszSrc, LPTSTR lpsz)
  1253. {
  1254.     UINT cch = lstrlen(lpsz);
  1255.     TCHAR ch;
  1256.  
  1257.     CharUpper (lpsz);
  1258.     CharUpper (lpszSrc);
  1259.     while (lpszSrc = LpszFindChar (lpszSrc, *lpsz))
  1260.     {
  1261.         if ((UINT)lstrlen(lpszSrc) < cch)
  1262.             break;
  1263.  
  1264.         ch = *(lpszSrc + cch);
  1265.         *(lpszSrc + cch) = 0;
  1266.  
  1267.         if (!lstrcmp (lpszSrc, lpsz))
  1268.             return TRUE;
  1269.  
  1270.         *(lpszSrc + cch) = ch;
  1271.         lpszSrc++;
  1272.     }
  1273.     return FALSE;
  1274. }
  1275.  
  1276.  
  1277. /*
  1278.  *  HrCheckExclusions()
  1279.  *
  1280.  *  Purpose:
  1281.  *
  1282.  *      Checks the message class against the list of message classes
  1283.  *      excluded from filtering.
  1284.  *
  1285.  *  Arguments:
  1286.  *
  1287.  *      lpsmh           the smh parent object
  1288.  *      lpmsg           the message to check for exclusion
  1289.  *
  1290.  *  Returns:
  1291.  *
  1292.  *      (HRESULT)       If the message is to be excluded, then hrSuccess
  1293.  *                      is returned, otherwize MAPI_E_NOT_ME indicates it
  1294.  *                      is OK to filter the message
  1295.  */
  1296. HRESULT
  1297. HrCheckExclusions (LPSMH lpsmh, LPMESSAGE lpmsg)
  1298. {
  1299.     SCODE sc;
  1300.     UINT isz = 0;
  1301.     LPSPropValue lpvalT = NULL;
  1302.  
  1303.     if (lpsmh->valEx.ulPropTag == PR_SMH_EXCLUSIONS)
  1304.     {
  1305.         if (!HR_FAILED (HrGetOneProp ((LPMAPIPROP)lpmsg, PR_MESSAGE_CLASS, &lpvalT)))
  1306.         {
  1307.             /* See if this is in the list of exclusions */
  1308.  
  1309.             for (isz = 0; isz < lpsmh->valEx.Value.MVSZ.cValues; isz++)
  1310.                 if (!lstrcmpi (lpvalT->Value.LPSZ, lpsmh->valEx.Value.MVSZ.LPPSZ[isz]))
  1311.                     break;
  1312.  
  1313.             (*lpsmh->lpfnFree) (lpvalT);
  1314.         }
  1315.         sc = ((isz == lpsmh->valEx.Value.MVSZ.cValues)
  1316.                     ? MAPI_E_NOT_ME
  1317.                     : S_OK);
  1318.     }
  1319.     else
  1320.         sc = MAPI_E_NOT_ME;
  1321.  
  1322.     DebugTraceSc (HrCheckExclusions(), sc);
  1323.     return ResultFromScode (sc);
  1324. }
  1325.  
  1326.  
  1327. /*
  1328.  *  HrCheckRule()
  1329.  *  
  1330.  *  Purpose:
  1331.  *  
  1332.  *      Examines a message to see if the current rule applies to the
  1333.  *      message. 
  1334.  *  
  1335.  *      IMPORTANT: If a rule is of type RL_SUBJECT, RL_FROM, or
  1336.  *      RL_ATTACH, then the set of properties required to check for
  1337.  *      matches with the message are retained and passed back to the
  1338.  *      caller such that subsequent calls to HrCheckRule() will not have
  1339.  *      to get those props a second time.  THEREFORE the caller is
  1340.  *      responsible for cleaning up any returned property values.
  1341.  *  
  1342.  *  Arguments:
  1343.  *  
  1344.  *      lprl            pointer to the rule
  1345.  *      lpmsg           pointer to the message to examine
  1346.  *      lppval  [OUT]   buffer containing the target entryid value struct
  1347.  *  
  1348.  *  Returns:
  1349.  *  
  1350.  *      (HRESULT)       If the rule does apply, hrSuccess is returned and
  1351.  *                      the lppval parameter will point to a lpval struct
  1352.  *                      containing the entryid of the target folder.
  1353.  *  
  1354.  *                      If the rule does not apply, the return value is
  1355.  *                      an HRESULT with MAPI_E_NOT_ME as the SCODE.
  1356.  *  
  1357.  *                      Otherwise normal error codes apply.
  1358.  */
  1359. HRESULT
  1360. HrCheckRule (LPSMH lpsmh, LPRULE lprl, LPMESSAGE lpmsg, LPSPropValue FAR * lppval)
  1361. {
  1362.     HRESULT hr;
  1363.     LPMAPITABLE lptbl = NULL;
  1364.     LPSPropValue lpval = *lppval;
  1365.     LPSPropValue lpvalT = NULL;
  1366.     ULONG cval;
  1367.  
  1368.     if (!lpval)
  1369.     {
  1370.         if ((lprl->rlTyp == RL_FROM) ||
  1371.             (lprl->rlTyp == RL_ATTACH) ||
  1372.             (lprl->rlTyp == RL_SUBJECT))
  1373.         {
  1374.             hr = lpmsg->lpVtbl->GetProps (lpmsg,
  1375.                                     (LPSPropTagArray)&sptMsgPrps,
  1376.                                     0,
  1377.                                     &cval,
  1378.                                     &lpval);
  1379.             if (HR_FAILED (hr))
  1380.                 goto ret;
  1381.         }
  1382.     }
  1383.  
  1384.     /* Init for failure */
  1385.  
  1386.     hr = ResultFromScode (MAPI_E_NOT_ME);
  1387.  
  1388.     if ((lprl->rlTyp == RL_TO) ||
  1389.         (lprl->rlTyp == RL_CC) ||
  1390.         (lprl->rlTyp == RL_BCC) ||
  1391.         (lprl->rlTyp == RL_RECIP))
  1392.     {
  1393.         hr = lpmsg->lpVtbl->GetRecipientTable (lpmsg, 0, &lptbl);
  1394.         if (HR_FAILED (hr))
  1395.             goto ret;
  1396.  
  1397.         hr = lptbl->lpVtbl->FindRow (lptbl, lprl->lpres, BOOKMARK_BEGINNING, 0);
  1398.         UlRelease (lptbl);
  1399.         if (HR_FAILED (hr) && (GetScode (hr) == MAPI_E_NOT_FOUND))
  1400.             hr = ResultFromScode (MAPI_E_NOT_ME);
  1401.     }
  1402.     else if (lprl->rlTyp == RL_SUBJECT)
  1403.     {
  1404.         if (lpval[ipSubj].ulPropTag == PR_SUBJECT)
  1405.         {
  1406.             if (FLpszContainsLpsz (lpval[ipSubj].Value.LPSZ, lprl->lpszData))
  1407.                 hr = hrSuccess;
  1408.         }
  1409.     }
  1410.     else if (lprl->rlTyp == RL_FROM)
  1411.     {
  1412.         if (lpval[ipSentRep].ulPropTag == PR_SENT_REPRESENTING_NAME)
  1413.         {
  1414.             if (FLpszContainsLpsz (lpval[ipSentRep].Value.LPSZ, lprl->lpszData))
  1415.                 hr = hrSuccess;
  1416.         }
  1417.         if (HR_FAILED (hr))
  1418.         {
  1419.             if (lpval[ipSentRepEA].ulPropTag == PR_SENT_REPRESENTING_EMAIL_ADDRESS)
  1420.             {
  1421.                 if (FLpszContainsLpsz (lpval[ipSentRepEA].Value.LPSZ, lprl->lpszData))
  1422.                     hr = hrSuccess;
  1423.             }
  1424.         }
  1425.     }
  1426.     else if (lprl->rlTyp == RL_ATTACH)
  1427.     {
  1428.         if (lpval[ipMsgFlgs].ulPropTag == PR_MESSAGE_FLAGS)
  1429.         {
  1430.             if (lpval[ipMsgFlgs].Value.l & MSGFLAG_HASATTACH)
  1431.                 hr = hrSuccess;
  1432.         }
  1433.     }
  1434.     else if (lprl->rlTyp == RL_BODY)
  1435.     {
  1436.         if (!HR_FAILED (HrGetOneProp ((LPMAPIPROP)lpmsg, PR_BODY, &lpvalT)))
  1437.         {
  1438.             if (FLpszContainsLpsz (lpvalT->Value.LPSZ, lprl->lpszData))
  1439.                 hr = hrSuccess;
  1440.  
  1441.             (*lpsmh->lpfnFree) (lpvalT);
  1442.         }
  1443.     }
  1444.     else if (lprl->rlTyp == RL_CLASS)
  1445.     {
  1446.         if (!HR_FAILED (HrGetOneProp ((LPMAPIPROP)lpmsg, PR_MESSAGE_CLASS, &lpvalT)))
  1447.         {
  1448.             if (FLpszContainsLpsz (lpvalT->Value.LPSZ, lprl->lpszData))
  1449.                 hr = hrSuccess;
  1450.  
  1451.             (*lpsmh->lpfnFree) (lpvalT);
  1452.         }
  1453.     }
  1454.  
  1455.     if (HR_FAILED (hr))
  1456.     {
  1457.         Assert (GetScode (hr) == MAPI_E_NOT_ME);
  1458.         if (lprl->ulFlags & RULE_NOT)
  1459.             hr = hrSuccess;
  1460.     }
  1461.  
  1462. ret:
  1463.  
  1464.     *lppval = lpval;
  1465.  
  1466.     DebugTraceResult (HrCheckRule(), hr);
  1467.     return hr;
  1468. }
  1469.  
  1470.  
  1471. /*
  1472.  *  HrFolderFromPath()
  1473.  *
  1474.  *  Purpose:
  1475.  *
  1476.  *      Takes a IPM root-based path string and returns a folder
  1477.  *      corresponding to the path given.  The '\' character is the path
  1478.  *      separator.  And non-existing folders are created as a psrt of the
  1479.  *      process.
  1480.  *
  1481.  *  Arguments:
  1482.  *
  1483.  *      lpsmh               pointer to smh parent object
  1484.  *      lpmdb               store in which the path is to exist
  1485.  *      lpszPath            the root-based path to use
  1486.  *      lppfldr     [OUT]   buffer to place target folder
  1487.  *      lppvalEid   [OUT]   buffer for target entryid value struct pointer
  1488.  *
  1489.  *  Returns:
  1490.  *
  1491.  *      (HRESULT)
  1492.  */
  1493. HRESULT
  1494. HrFolderFromPath (LPSMH lpsmh,
  1495.     LPMDB lpmdb,
  1496.     LPTSTR lpszPath,
  1497.     LPMAPIFOLDER FAR * lppfldr,
  1498.     LPSPropValue FAR * lppvalEid)
  1499. {
  1500.     HRESULT hr;
  1501.     LPMAPIFOLDER lpfldr = NULL;
  1502.     LPMAPIFOLDER lpfldrT = NULL;
  1503.     LPSPropValue lpval = NULL;
  1504.     LPTSTR lpch;
  1505.     TCHAR rgch[MAX_PATH];
  1506.     ULONG ulType;
  1507.  
  1508.     if (!LoadString (lpsmh->hinst, SMH_FolderComment, rgch, sizeof(rgch)))
  1509.         rgch[0] = 0;
  1510.  
  1511.     hr = HrGetOneProp ((LPMAPIPROP)lpmdb, PR_IPM_SUBTREE_ENTRYID, &lpval);
  1512.     if (!HR_FAILED (hr))
  1513.     {
  1514.         hr = lpmdb->lpVtbl->OpenEntry (lpmdb,
  1515.                             lpval->Value.bin.cb,
  1516.                             (LPENTRYID)lpval->Value.bin.lpb,
  1517.                             NULL,
  1518.                             MAPI_MODIFY,
  1519.                             &ulType,
  1520.                             (LPUNKNOWN FAR *)&lpfldr);
  1521.  
  1522.         (*lpsmh->lpfnFree) (lpval);
  1523.         lpval = NULL;
  1524.  
  1525.         if (!HR_FAILED (hr))
  1526.         {
  1527.             do
  1528.             {
  1529.                 if (lpch = LpszFindChar (lpszPath, '\\'))
  1530.                     *lpch = 0;
  1531.  
  1532.                 Assert (lstrlen (lpszPath));
  1533.                 hr = lpfldr->lpVtbl->CreateFolder (lpfldr,
  1534.                             FOLDER_GENERIC,
  1535.                             lpszPath,
  1536.                             rgch,
  1537.                             NULL,
  1538.                             MAPI_MODIFY | OPEN_IF_EXISTS,
  1539.                             &lpfldrT);
  1540.                 if (HR_FAILED (hr))
  1541.                 {
  1542. #ifdef  DEBUG
  1543.                     LPMAPIERROR lperr = NULL;
  1544.                     lpfldr->lpVtbl->GetLastError (lpfldr, hr, 0, &lperr);
  1545.                     DebugTrace ("SMH: WARNING: unable to open/create folder: '%s' in %s\n",
  1546.                         lperr->lpszError, lperr->lpszComponent);
  1547.                     (*lpsmh->lpfnFree) (lperr);
  1548. #endif
  1549.                     break;
  1550.                 }
  1551.  
  1552.                 UlRelease (lpfldr);
  1553.                 lpfldr = lpfldrT;
  1554.                 lpfldrT = NULL;
  1555.  
  1556.                 lpszPath = (lpch ? ++lpch : NULL);
  1557.  
  1558.             } while (lpszPath);
  1559.         }
  1560.         if (!HR_FAILED (hr))
  1561.         {
  1562.             hr = HrGetOneProp ((LPMAPIPROP)lpfldr, PR_ENTRYID, &lpval);
  1563.             if (!HR_FAILED (hr))
  1564.             {
  1565.                 *lppfldr = lpfldr;
  1566.                 *lppvalEid = lpval;
  1567.                 lpfldr = NULL;
  1568.                 lpval = NULL;
  1569.             }
  1570.         }
  1571.     }
  1572.  
  1573.     (*lpsmh->lpfnFree) (lpval);
  1574.     UlRelease (lpfldr);
  1575.  
  1576.     DebugTraceResult (HrFolderFromPath(), hr);
  1577.     return hr;
  1578. }
  1579.  
  1580.  
  1581. /*
  1582.  *  HrBuildRule()
  1583.  *
  1584.  *  Purpose:
  1585.  *
  1586.  *      Takes a profile section and builds a rule structure that
  1587.  *      corresponds to the properties in the profile section.
  1588.  *
  1589.  *  Arguments:
  1590.  *
  1591.  *      lpsmh           pointer to smh parent object
  1592.  *      lpmuid          profile section UID
  1593.  *      lpprl   [OUT]   buffer for the newly created rule pointer
  1594.  *
  1595.  *  Returns:
  1596.  *
  1597.  *      (HRESULT)
  1598.  */
  1599. HRESULT
  1600. HrBuildRule (LPSMH lpsmh, LPMAPIUID lpmuid, LPRULE FAR * lpprl)
  1601. {
  1602.     SCODE sc;
  1603.     HRESULT hr;
  1604.     LPMAPISESSION lpsess = lpsmh->lpsess;
  1605.     LPPROFSECT lpprof = NULL;
  1606.     LPRULE lprl = NULL;
  1607.     LPSPropValue lpval = NULL;
  1608.     LPSPropValue lpvalEid = NULL;
  1609.     LPSPropValue lpvalT;
  1610.     LPSRestriction lpres = NULL;
  1611.     ULONG cval;
  1612.     ULONG ulType;
  1613.     UINT cb;
  1614.  
  1615.     sc = (*lpsmh->lpfnAlloc) (sizeof(RULE), &lprl);
  1616.     if (FAILED (sc))
  1617.     {
  1618.         hr = ResultFromScode (sc);
  1619.         goto ret;
  1620.     }
  1621.     memset (lprl, 0, sizeof(RULE));
  1622.     memcpy (&lprl->muid, lpmuid, sizeof(MAPIUID));
  1623.     hr = lpsess->lpVtbl->OpenProfileSection (lpsess,
  1624.                             lpmuid,
  1625.                             NULL,
  1626.                             MAPI_MODIFY,
  1627.                             &lpprof);
  1628.     if (HR_FAILED (hr))
  1629.         goto ret;
  1630.  
  1631.     hr = lpprof->lpVtbl->GetProps (lpprof,
  1632.                             (LPSPropTagArray)&sptRule,
  1633.                             0,
  1634.                             &cval,
  1635.                             &lpval);
  1636.     if (HR_FAILED (hr))
  1637.         goto ret;
  1638.  
  1639.     if ((lpval[ipType].ulPropTag != PR_RULE_TYPE) ||
  1640.         (lpval[ipData].ulPropTag != PR_RULE_DATA) ||
  1641.         (lpval[ipRLFlags].ulPropTag != PR_RULE_FLAGS) ||
  1642.         (!(lpval[ipRLFlags].Value.l & RULE_DELETE) &&
  1643.           (lpval[ipType].ulPropTag != PR_RULE_TYPE) ||
  1644.           (lpval[ipPath].ulPropTag != PR_RULE_TARGET_PATH) ||
  1645.           (lpval[ipStore].ulPropTag != PR_RULE_STORE_DISPLAY_NAME)))
  1646.     {
  1647.         /* Something very important is missing */
  1648.  
  1649.         hr = ResultFromScode (MAPI_E_UNCONFIGURED);
  1650.         goto ret;
  1651.     }
  1652.     lprl->rlTyp = (UINT)lpval[ipType].Value.l;
  1653.     lprl->ulFlags = lpval[ipRLFlags].Value.l;
  1654.     memcpy (lprl->lpszData, lpval[ipData].Value.bin.lpb, (UINT)lpval[ipData].Value.bin.cb);
  1655.  
  1656.     if (lpval[ipSound].ulPropTag == PR_RULE_SOUND_FILENAME)
  1657.         lstrcpy (lprl->lpszSound, lpval[ipSound].Value.lpszA);
  1658.  
  1659.     if (!(lpval[ipRLFlags].Value.l & RULE_DELETE))
  1660.     {
  1661.         hr = HrOpenMdbFromName (lpsmh, lpval[ipStore].Value.LPSZ, &lprl->lpmdb);
  1662.         if (HR_FAILED (hr))
  1663.             goto ret;
  1664.  
  1665.         if ((lpval[ipEID].ulPropTag != PR_RULE_TARGET_ENTRYID) ||
  1666.             (HR_FAILED (lpsess->lpVtbl->OpenEntry (lpsess,
  1667.                             lpval[ipEID].Value.bin.cb,
  1668.                             (LPENTRYID)lpval[ipEID].Value.bin.lpb,
  1669.                             NULL,
  1670.                             MAPI_MODIFY,
  1671.                             &ulType,
  1672.                             (LPUNKNOWN FAR *)&lprl->lpfldr))))
  1673.         {
  1674.             hr = HrFolderFromPath (lpsmh,
  1675.                             lprl->lpmdb,
  1676.                             lpval[ipPath].Value.LPSZ,
  1677.                             &lprl->lpfldr,
  1678.                             &lpvalEid);
  1679.             if (HR_FAILED (hr))
  1680.                 goto ret;
  1681.  
  1682.             lpvalEid->ulPropTag = PR_RULE_TARGET_ENTRYID;
  1683.             HrSetOneProp ((LPMAPIPROP)lpprof, lpvalEid);
  1684.             lprl->lpvalEid = lpvalEid;
  1685.         }
  1686.         else
  1687.         {
  1688.             hr = HrGetOneProp ((LPMAPIPROP)lprl->lpfldr, PR_ENTRYID, &lprl->lpvalEid);
  1689.             if (HR_FAILED (hr))
  1690.                 goto ret;
  1691.         }
  1692.     }
  1693.  
  1694.     if ((lpval[ipType].Value.l == RL_TO) ||
  1695.         (lpval[ipType].Value.l == RL_CC) ||
  1696.         (lpval[ipType].Value.l == RL_BCC) ||
  1697.         (lpval[ipType].Value.l == RL_RECIP))
  1698.     {
  1699.         cb = (sizeof(SRestriction) * cresMax) + (sizeof(SPropValue) * cvMax);
  1700.         sc = (*lpsmh->lpfnAllocMore) (cb, lprl, &lpres);
  1701.         if (FAILED (sc))
  1702.         {
  1703.             hr = ResultFromScode (sc);
  1704.             goto ret;
  1705.         }
  1706.         lpvalT = (LPSPropValue)&lpres[cresMax];
  1707.  
  1708.         lpres[iresAnd].rt = RES_AND;
  1709.         lpres[iresAnd].res.resAnd.cRes = 2;
  1710.         lpres[iresAnd].res.resAnd.lpRes = &lpres[iresRecip];
  1711.  
  1712.         lpvalT[ivRecip].ulPropTag = PR_RECIPIENT_TYPE;
  1713.         lpvalT[ivRecip].Value.l = ((lpval[ipType].Value.l == RL_TO)
  1714.                                     ? MAPI_TO
  1715.                                     : ((lpval[ipType].Value.l == RL_CC)
  1716.                                         ? MAPI_CC
  1717.                                         : ((lpval[ipType].Value.l == RL_BCC)
  1718.                                             ? MAPI_BCC
  1719.                                             : 0)));
  1720.         lpres[iresRecip].rt = RES_PROPERTY;
  1721.         lpres[iresRecip].res.resProperty.relop = RELOP_EQ;
  1722.         lpres[iresRecip].res.resContent.ulPropTag = PR_RECIPIENT_TYPE;
  1723.         lpres[iresRecip].res.resContent.lpProp = &lpvalT[ivRecip];
  1724.  
  1725.         lpres[iresOr].rt = RES_OR;
  1726.         lpres[iresOr].res.resOr.cRes = 2;
  1727.         lpres[iresOr].res.resOr.lpRes = &lpres[iresEmail];
  1728.  
  1729.         lpvalT[ivEmail].ulPropTag = PR_EMAIL_ADDRESS;
  1730.         lpvalT[ivEmail].Value.LPSZ = (LPTSTR)lprl->lpszData;
  1731.         lpres[iresEmail].rt = RES_CONTENT;
  1732.         lpres[iresEmail].res.resContent.ulFuzzyLevel = FL_SUBSTRING | FL_IGNORECASE;
  1733.         lpres[iresEmail].res.resContent.ulPropTag = PR_EMAIL_ADDRESS;
  1734.         lpres[iresEmail].res.resContent.lpProp = &lpvalT[ivEmail];
  1735.  
  1736.         lpvalT[ivDispNm].ulPropTag = PR_DISPLAY_NAME;
  1737.         lpvalT[ivDispNm].Value.LPSZ = (LPTSTR)lprl->lpszData;
  1738.         lpres[iresDispNm].rt = RES_CONTENT;
  1739.         lpres[iresDispNm].res.resContent.ulFuzzyLevel = FL_SUBSTRING | FL_IGNORECASE;
  1740.         lpres[iresDispNm].res.resContent.ulPropTag = PR_DISPLAY_NAME;
  1741.         lpres[iresDispNm].res.resContent.lpProp = &lpvalT[ivDispNm];
  1742.  
  1743.         if ((lpval[ipType].Value.l == RL_TO) ||
  1744.             (lpval[ipType].Value.l == RL_CC) ||
  1745.             (lpval[ipType].Value.l == RL_BCC))
  1746.             lprl->lpres = &lpres[iresAnd];
  1747.         else
  1748.             lprl->lpres = &lpres[iresOr];
  1749.     }
  1750.  
  1751. ret:
  1752.  
  1753.     (*lpsmh->lpfnFree) (lpval);
  1754.  
  1755.     UlRelease (lpprof);
  1756.     if (HR_FAILED (hr))
  1757.     {
  1758.         (*lpsmh->lpfnFree) (lprl->lpvalEid);
  1759.         (*lpsmh->lpfnFree) (lprl);
  1760.         lprl = NULL;
  1761.     }
  1762.     *lpprl = lprl;
  1763.  
  1764.     DebugTraceResult (HrBuildRule(), hr);
  1765.     return hr;
  1766. }
  1767.  
  1768.  
  1769. /*
  1770.  *  ReleaseBkit()
  1771.  *
  1772.  *  Purpose:
  1773.  *
  1774.  *      Cleans up all resources held by a bucket structure and wipes the
  1775.  *      structure clean.
  1776.  *
  1777.  *  Arguments:
  1778.  *
  1779.  *      lpsmh           pointer to the smh object (uses allocation fn's)
  1780.  *      lpbkit          pointer to the bucket needing cleaning
  1781.  *
  1782.  */
  1783. VOID
  1784. ReleaseBkit (LPSMH lpsmh, LPBKIT lpbkit)
  1785. {
  1786.     UlRelease (lpbkit->lpfldr);
  1787.     UlRelease (lpbkit->lpfldrYr);
  1788.     UlRelease (lpbkit->lpfldrParent);
  1789.     (*lpsmh->lpfnFree) (lpbkit->lpeid);
  1790.     (*lpsmh->lpfnFree) (lpbkit->lpeidYr);
  1791.     (*lpsmh->lpfnFree) (lpbkit->lpeidParent);
  1792.     memset (lpbkit, 0, sizeof(BKIT));
  1793.     return;
  1794. }
  1795.  
  1796.  
  1797. /*
  1798.  *  CleanupSMH()
  1799.  *
  1800.  *  Purpose:
  1801.  *
  1802.  *      Cleans up all resources and objects held by the SMH object.
  1803.  *
  1804.  *  Arguments:
  1805.  *
  1806.  *      lpsmh           the SMH parent object
  1807.  */
  1808. VOID
  1809. CleanupSMH (LPSMH lpsmh)
  1810. {
  1811.     LPRULE lprl;
  1812.     LPRULE lprlT;
  1813.     LPWB lpwb;
  1814.     LPWB lpwbT;
  1815.  
  1816.     ReleaseBkit (lpsmh, &lpsmh->bkitSm);
  1817.     memset (&lpsmh->bkitSm, 0, sizeof(BKIT));
  1818.  
  1819.     for (lpwb = lpsmh->lstWb; lpwb; lpwb = lpwbT)
  1820.     {
  1821.         lpwbT = lpwb->wbNext;
  1822.         lpwb->lptbl->lpVtbl->Unadvise (lpwb->lptbl, lpwb->ulAdvz);
  1823.         ReleaseBkit (lpsmh, &lpwb->bkit);
  1824.         UlRelease (lpwb->lptbl);
  1825.         UlRelease (lpwb->lpfldr);
  1826.         (*lpsmh->lpfnFree) (lpwb->lpvalEid);
  1827.         (*lpsmh->lpfnFree) (lpwb);
  1828.     }
  1829.     lpsmh->lstWb = NULL;
  1830.     for (lprl = lpsmh->lstRl; lprl; lprl = lprlT)
  1831.     {
  1832.         lprlT = lprl->rlNext;
  1833.         ReleaseBkit (lpsmh, &lprl->bkit);
  1834.         UlRelease (lprl->lpfldr);
  1835.         (*lpsmh->lpfnFree) (lprl->lpvalEid);
  1836.         (*lpsmh->lpfnFree) (lprl);
  1837.  
  1838.     }
  1839.     lpsmh->lstRl = NULL;
  1840.     ReleaseStoresTable (lpsmh);
  1841. }
  1842.  
  1843.  
  1844. /*
  1845.  *  HrInitSMH()
  1846.  *
  1847.  *  Purpose:
  1848.  *
  1849.  *      Initializes the SMH stucture, loads all filters, loads all
  1850.  *      exclusions, creates any unread search folders, and inits all
  1851.  *      wastebasket objects -- all based on the profile section.
  1852.  *
  1853.  *  Arguments:
  1854.  *
  1855.  *      lpsmh           the SMH parent object
  1856.  *
  1857.  *  Returns:
  1858.  *
  1859.  *      (HRESULT)
  1860.  */
  1861. HRESULT
  1862. HrInitSMH (LPSMH lpsmh)
  1863. {
  1864.     HRESULT hr;
  1865.     LPMAPISESSION lpsess = lpsmh->lpsess;
  1866.     LPMAPIUID lpmuid = &lpsmh->muid;
  1867.     LPPROFSECT lpprof = NULL;
  1868.     LPSPropValue lpval = NULL;
  1869.     ULONG cval;
  1870.  
  1871.     /* Get options from the profile */
  1872.  
  1873.     hr = lpsess->lpVtbl->OpenProfileSection (lpsess,
  1874.                             lpmuid,
  1875.                             NULL,
  1876.                             MAPI_MODIFY,
  1877.                             &lpprof);
  1878.     if (HR_FAILED (hr))
  1879.         goto ret;
  1880.  
  1881.     hr = lpprof->lpVtbl->GetProps (lpprof,
  1882.                             (LPSPropTagArray)&sptConfigProps,
  1883.                             0,
  1884.                             &cval,
  1885.                             &lpval);
  1886.     UlRelease (lpprof);
  1887.     if (HR_FAILED (hr))
  1888.         goto ret;
  1889.  
  1890.     /* Check to see if we are configured */
  1891.  
  1892.     if (REQ_PROPS != (UlChkReqProps (cval, lpval) & REQ_PROPS))
  1893.     {
  1894.         hr = ResultFromScode (MAPI_E_UNCONFIGURED);
  1895.         goto ret;
  1896.     }
  1897.  
  1898.     /* Grab the exclusions list */
  1899.  
  1900.     hr = ResultFromScode (PropCopyMore (&lpsmh->valEx,
  1901.                             &lpval[ipExc],
  1902.                             lpsmh->lpfnAllocMore,
  1903.                             lpsmh));
  1904.     if (HR_FAILED (hr))
  1905.         goto ret;
  1906.  
  1907.     /* Init the stores table */
  1908.  
  1909.     hr = HrInitStoresTable (lpsmh, lpsess);
  1910.     if (HR_FAILED (hr))
  1911.         goto ret;
  1912.  
  1913.     /* Store the values */
  1914.  
  1915.     lpsmh->fCatSm = !!(lpval[ipSMHFlags].Value.l & SMH_FILTER_SENTMAIL);
  1916.     lpsmh->fCatSmByYr = !!(lpval[ipSMHFlags].Value.l & SMH_FILTER_SENTMAIL_YR);
  1917.     if (lpval[ipSMHFlags].Value.l & SMH_FILTER_DELETED)
  1918.     {
  1919.         HrInitDeletedMailFilter (lpsmh);
  1920.         lpsmh->fCatWb = !!(lpval[ipSMHFlags].Value.l & SMH_FILTER_DELETED_YR);
  1921.     }
  1922.     if (!!(lpval[ipSMHFlags].Value.l & SMH_UNREAD_VIEWER))
  1923.         HrInitUnreadSearch (lpsmh);
  1924.  
  1925.     if ((lpval[ipSMHFlags].Value.l & SMH_FILTER_INBOUND) &&
  1926.         (lpval[ipRules].ulPropTag == PR_SMH_RULES) &&
  1927.         (lpval[ipRules].Value.bin.cb != 0))
  1928.     {
  1929.         UINT crl = (UINT)(lpval[ipRules].Value.bin.cb / sizeof(MAPIUID));
  1930.         LPMAPIUID lpmuid = ((LPMAPIUID)lpval[ipRules].Value.bin.lpb) + crl;
  1931.         LPRULE lprl;
  1932.  
  1933.         while (crl--)
  1934.         {
  1935.             --lpmuid;
  1936.             hr = HrBuildRule (lpsmh, lpmuid, &lprl);
  1937.             if (!HR_FAILED (hr))
  1938.             {
  1939.                 lprl->rlNext = lpsmh->lstRl;
  1940.                 lpsmh->lstRl = lprl;
  1941.             }
  1942.         }
  1943.         hr = hrSuccess;
  1944.     }
  1945.  
  1946. ret:
  1947.  
  1948.     (*lpsmh->lpfnFree) (lpval);
  1949.     DebugTraceResult (HrInitSMH(), hr);
  1950.     return hr;
  1951. }
  1952.  
  1953.  
  1954. /*
  1955.  *  HrReConfigSMH()
  1956.  *
  1957.  *  Purpose:
  1958.  *
  1959.  *      Cleans up the current SMH configuration and reloads it from
  1960.  *      scratch.
  1961.  *
  1962.  *  Arguments:
  1963.  *
  1964.  *      lpsmh           the SMH parent object
  1965.  *
  1966.  *  Returns:
  1967.  *
  1968.  *      (HRESULT)
  1969.  */
  1970. HRESULT
  1971. HrReConfigSMH (LPSMH lpsmh)
  1972. {
  1973.     HRESULT hr;
  1974.  
  1975.     CleanupSMH (lpsmh);
  1976.     hr = HrInitSMH (lpsmh);
  1977.     if (!HR_FAILED (hr))
  1978.         ResetConfigEvent (lpsmh->hevtConfig);
  1979.  
  1980.     DebugTraceResult (HrReconfigSMH(), hr);
  1981.     return hr;
  1982. }
  1983.  
  1984.  
  1985. /*
  1986.  *  SMH Object Methods
  1987.  *
  1988.  *  SMH_QueryInterface      (See OLE IUnknown object methods)
  1989.  *  SMH_AddRef              (See OLE IUnknown object methods)
  1990.  *  SMH_Release             (See OLE IUnknown object methods)
  1991.  *  SMH_InboundMsgHook      Filters inbound messages
  1992.  *  SMH_OutboundMsgHook     Filters sent mail messages
  1993.  *
  1994.  */
  1995. STDMETHODIMP
  1996. SMH_QueryInterface (LPSMH lpsmh, REFIID lpiid, LPVOID FAR * lppv)
  1997. {
  1998.     if (IsBadWritePtr (lpsmh, sizeof(SMH)) ||
  1999.         IsBadReadPtr (lpiid, sizeof(IID)) ||
  2000.         IsBadWritePtr (lppv, sizeof(LPVOID)))
  2001.         return ResultFromScode (MAPI_E_INVALID_PARAMETER);
  2002.  
  2003.     if (!memcmp (lpiid, &IID_ISpoolerHook, sizeof(IID)) ||
  2004.         !memcmp (lpiid, &IID_IUnknown, sizeof(IID)))
  2005.     {
  2006.         *lppv = (LPVOID)lpsmh;
  2007.         lpsmh->lcInit++;
  2008.         return hrSuccess;
  2009.     }
  2010.     DebugTraceSc (SMH_QueryInterface(), MAPI_E_INTERFACE_NOT_SUPPORTED);
  2011.     return ResultFromScode (MAPI_E_INTERFACE_NOT_SUPPORTED);
  2012. }
  2013.  
  2014. STDMETHODIMP_ (ULONG)
  2015. SMH_AddRef (LPSMH lpsmh)
  2016. {
  2017.     if (IsBadWritePtr (lpsmh, sizeof(SMH)))
  2018.         return 0;
  2019.  
  2020.     return ++lpsmh->lcInit;
  2021. }
  2022.  
  2023. STDMETHODIMP_ (ULONG)
  2024. SMH_Release (LPSMH lpsmh)
  2025. {
  2026.     if (IsBadWritePtr (lpsmh, sizeof(SMH)))
  2027.         return 0;
  2028.  
  2029.     if (--lpsmh->lcInit)
  2030.         return lpsmh->lcInit;
  2031.  
  2032.     CloseConfigEvent (lpsmh->hevtConfig);
  2033.     CleanupSMH (lpsmh);
  2034.     UlRelease (lpsmh->lpsess);
  2035.     (*lpsmh->lpfnFree) (lpsmh);
  2036.     return 0;
  2037. }
  2038.  
  2039. /*
  2040.  *  SMH_InboundMsgHook()
  2041.  *
  2042.  *  Purpose:
  2043.  *
  2044.  *      The purpose of this filter is to match inbound messages to
  2045.  *      individual rules from the profile and re-route the messages based
  2046.  *      on the results of the comparisons.
  2047.  *
  2048.  *  Arguments:
  2049.  *
  2050.  *      lpsmh           this filter hook obj
  2051.  *      lpmsg           the message to be filtered
  2052.  *      lpfldrDef       owning folder of message
  2053.  *      lpmdbDef        owning store of message
  2054.  *      lpulFlags       flags returned by filter
  2055.  *      lpcbeid         cb for entryid of default target for message
  2056.  *      lppbeid         pb for entryid of default target for message
  2057.  *
  2058.  *  Operation:
  2059.  *
  2060.  *      Opens the suggested folder (if needed) and checks for the
  2061.  *      existence of the appropriate "bucket" folder.  If it does exist,
  2062.  *      then the  folder is created and cached.  The entryid is grabbed
  2063.  *      and passed back in to the spooler.
  2064.  *
  2065.  *  Returns:
  2066.  *
  2067.  *      (HRESULT)
  2068.  *      lpulFlags   [out]   set HOOK_CANCEL if this is the last hook
  2069.  *      lpcbeid     [out]   the size of the returned EntryID
  2070.  *      lppbeid     [out]   the data of the returned EntryID
  2071.  *
  2072.  */
  2073. STDMETHODIMP
  2074. SMH_InboundMsgHook (LPSMH lpsmh,
  2075.     LPMESSAGE lpmsg,
  2076.     LPMAPIFOLDER lpfldrDef,
  2077.     LPMDB lpmdbDef,
  2078.     ULONG FAR * lpulFlags,
  2079.     ULONG FAR * lpcbeid,
  2080.     LPBYTE FAR * lppeid)
  2081. {
  2082.     HRESULT hr = hrSuccess;
  2083.     LPRULE lprl;
  2084.     LPBYTE lpeid;
  2085.     LPSPropValue lpval = NULL;
  2086.  
  2087.     /* Quick and dirty parameter check */
  2088.  
  2089.     if (IsBadWritePtr (lpsmh, sizeof(SMH)) ||
  2090.         IsBadWritePtr (lpcbeid, sizeof(ULONG)) ||
  2091.         IsBadWritePtr (lppeid, sizeof(LPBYTE)) ||
  2092.         IsBadWritePtr (*lppeid, (UINT)(*lpcbeid)))
  2093.         return ResultFromScode (MAPI_E_INVALID_PARAMETER);
  2094.  
  2095.     if (FConfigChanged (lpsmh->hevtConfig) &&
  2096.         HR_FAILED (hr = HrReConfigSMH (lpsmh)))
  2097.     {
  2098.         DebugTraceResult (SMH_InboundMsgHook(), hr);
  2099.         return hrSuccess;
  2100.     }
  2101.  
  2102.     if (lprl = lpsmh->lstRl)    /* Yup '=' */
  2103.     {
  2104.         hr = HrCheckExclusions (lpsmh, lpmsg);
  2105.         if (HR_FAILED (hr))
  2106.         {
  2107.             /* We have not been excluded */
  2108.  
  2109.             for (; lprl; lprl = lprl->rlNext)
  2110.             {
  2111.                 hr = HrCheckRule (lpsmh, lprl, lpmsg, &lpval);
  2112.                 if (!HR_FAILED (hr))
  2113.                 {
  2114.                     /* We have a match. What do we do, filter or delete? */
  2115.                     
  2116.                     if (!(lprl->ulFlags & RULE_DELETE))
  2117.                     {
  2118.                         /* Filter the critter */
  2119.  
  2120.                         hr = ResultFromScode ((*lpsmh->lpfnAlloc) (lprl->lpvalEid->Value.bin.cb, &lpeid));
  2121.                         if (!HR_FAILED (hr))
  2122.                         {
  2123.                             memcpy (lpeid, lprl->lpvalEid->Value.bin.lpb,
  2124.                                 (UINT)lprl->lpvalEid->Value.bin.cb);
  2125.  
  2126.                             (*lpsmh->lpfnFree) (*lppeid);
  2127.                             *lpcbeid = lprl->lpvalEid->Value.bin.cb;
  2128.                             *lppeid = lpeid;
  2129.  
  2130.                             if (lprl->ulFlags & RULE_ARCHIVED)
  2131.                             {
  2132.                                 hr = HrArchiveMessage (lpsmh,
  2133.                                             lpmsg,
  2134.                                             lpfldrDef,
  2135.                                             lpmdbDef,
  2136.                                             &lprl->bkit,
  2137.                                             !!(lprl->ulFlags & RULE_ARCHIVED_BY_YEAR),
  2138.                                             lpcbeid,
  2139.                                             lppeid);
  2140.                                 
  2141.                                 if (lprl->ulFlags & RULE_TERMINAL)
  2142.                                     *lpulFlags = HOOK_CANCEL;
  2143.                             }
  2144.                         }
  2145.  
  2146.                         if (lstrlen (lprl->lpszSound))
  2147.                             sndPlaySound (lprl->lpszSound, SND_ASYNC);
  2148.                     }
  2149.                     else
  2150.                         *lpulFlags = HOOK_DELETE | HOOK_CANCEL;
  2151.  
  2152.                     break;
  2153.                 }
  2154.                 else if (GetScode (hr) != MAPI_E_NOT_ME)
  2155.                 {
  2156.                     /*  We have a failure that is not really
  2157.                      *  expected, we need to bail.  Also, this
  2158.                      *  should cancel any further hooking
  2159.                      */
  2160.                     *lpulFlags = HOOK_CANCEL;
  2161.                     break;
  2162.                 }
  2163.                 else
  2164.                     hr = hrSuccess;
  2165.             }
  2166.         }
  2167.     }
  2168.  
  2169.     (*lpsmh->lpfnFree) (lpval);
  2170.     DebugTraceResult (SMH_InboundMsgHook(), hr);
  2171.     return hrSuccess;
  2172. }
  2173.  
  2174.  
  2175. /*
  2176.  *  SMH_OutboundMsgHook()
  2177.  *
  2178.  *  Purpose:
  2179.  *
  2180.  *      The purpose of this filter is to "hash" a users sent mail
  2181.  *      processing based on date.  The most obvious bucket size is
  2182.  *      monthly but there is no reason not to make this an option the
  2183.  *      user could confiigure.
  2184.  *
  2185.  *  Arguments:
  2186.  *
  2187.  *      lpsmh           this filter hook obj
  2188.  *      lpmsg           the message to be filtered
  2189.  *      lpfldrDef       owning folder of message
  2190.  *      lpmdbDef        owning store of message
  2191.  *      lpulFlags       flags returned by filter
  2192.  *      lpcbeid         cb for entryid of default target for message
  2193.  *      lppbeid         pb for entryid of default target for message
  2194.  *
  2195.  *  Operation:
  2196.  *
  2197.  *      Opens the suggested folder (if needed) and checks for the
  2198.  *      existence of the appropriate "bucket" folder.  If it does exist,
  2199.  *      then the  folder is created and cached.  The entryid is grabbed
  2200.  *      and passed back in to the spooler.
  2201.  *
  2202.  *  Returns:
  2203.  *
  2204.  *      (HRESULT)
  2205.  *      lpulFlags   [out]   set HOOK_CANCEL if this is the last hook
  2206.  *      lpcbeid     [out]   the size of the returned EntryID
  2207.  *      lppbeid     [out]   the data of the returned EntryID
  2208.  *
  2209.  */
  2210. STDMETHODIMP
  2211. SMH_OutboundMsgHook (LPSMH lpsmh,
  2212.     LPMESSAGE lpmsg,
  2213.     LPMAPIFOLDER lpfldrDef,
  2214.     LPMDB lpmdbDef,
  2215.     ULONG FAR * lpulFlags,
  2216.     ULONG FAR * lpcbeid,
  2217.     LPBYTE FAR * lppeid)
  2218. {
  2219.     HRESULT hr = hrSuccess;
  2220.  
  2221.     /* Quick and dirty parameter check */
  2222.  
  2223.     if (IsBadWritePtr (lpsmh, sizeof(SMH)) ||
  2224.         IsBadWritePtr (lpcbeid, sizeof(ULONG)) ||
  2225.         IsBadWritePtr (lppeid, sizeof(LPBYTE)) ||
  2226.         IsBadWritePtr (*lppeid, (UINT)(*lpcbeid)))
  2227.         return ResultFromScode (MAPI_E_INVALID_PARAMETER);
  2228.  
  2229.     if (FConfigChanged (lpsmh->hevtConfig) &&
  2230.         HR_FAILED (hr = HrReConfigSMH (lpsmh)))
  2231.     {
  2232.         DebugTraceResult (SMH_InboundMsgHook(), hr);
  2233.         return hrSuccess;
  2234.     }
  2235.  
  2236.     if (lpsmh->fCatSm)
  2237.         hr = HrArchiveMessage (lpsmh,
  2238.                 lpmsg,
  2239.                 lpfldrDef,
  2240.                 lpmdbDef,
  2241.                 &lpsmh->bkitSm,
  2242.                 lpsmh->fCatSmByYr,
  2243.                 lpcbeid,
  2244.                 lppeid);
  2245.  
  2246.     DebugTraceResult (SMH_OutboundMsgHook(), hr);
  2247.     return hrSuccess;
  2248. }
  2249.  
  2250.  
  2251. /*
  2252.  *  SMH_Init()
  2253.  *
  2254.  *  Purpose:
  2255.  *
  2256.  *      Spooler's entry into the sample mail handler.  This function is
  2257.  *      equivilent to a provider logon in that it returns an object to
  2258.  *      the spooler that will be used to make any additional calls into
  2259.  *      the handler.
  2260.  *
  2261.  *  Arguments:
  2262.  *
  2263.  *      lpsess          the session this handler relates to
  2264.  *      hinst           hinst of the SMH dll
  2265.  *      lpfnAlloc       pointer to MAPIAllocateBuffer()
  2266.  *      lpfnAllocMore   pointer to MAPIAllocateMore()
  2267.  *      lpfnFree        pointer to MAPIFreeBuffer()
  2268.  *      lpmuid          pointer to profile section muid
  2269.  *      ulFlags         flags
  2270.  *      lppHook         buffer to hold handler object
  2271.  *
  2272.  *  Returns:
  2273.  *
  2274.  *      (HRESULT)
  2275.  *      lpphook     [OUT]   holds the returned handler object iff successful
  2276.  */
  2277. STDINITMETHODIMP
  2278. SMH_Init (LPMAPISESSION lpsess,
  2279.     HINSTANCE hinst,
  2280.     LPALLOCATEBUFFER lpfnAlloc,
  2281.     LPALLOCATEMORE lpfnAllocMore,
  2282.     LPFREEBUFFER lpfnFree,
  2283.     LPMAPIUID lpmuid,
  2284.     ULONG ulFlags,
  2285.     LPSPOOLERHOOK FAR * lppHook)
  2286. {
  2287.     SCODE sc;
  2288.     HRESULT hr;
  2289.     LPSMH lpsmh = NULL;
  2290.  
  2291.     sc = (*lpfnAlloc) (sizeof(SMH), &lpsmh);
  2292.     if (FAILED (sc))
  2293.         return ResultFromScode (sc);
  2294.     memset (lpsmh, 0, sizeof(SMH));
  2295.  
  2296.     hr = lpsess->lpVtbl->QueryInterface (lpsess,
  2297.                             &IID_IMAPISession,
  2298.                             &lpsmh->lpsess);
  2299.     if (HR_FAILED (hr))
  2300.     {
  2301.         (*lpfnFree) (lpsmh);
  2302.         lpsmh = NULL;
  2303.         goto ret;
  2304.     }
  2305.  
  2306.     hr = HrGetConfigEvent (&lpsmh->hevtConfig);
  2307.     if (HR_FAILED (hr))
  2308.     {
  2309.         (*lpfnFree) (lpsmh);
  2310.         lpsmh = NULL;
  2311.         goto ret;
  2312.     }
  2313.  
  2314.     /* Fill in all fields of the object */
  2315.  
  2316.     lpsmh->lpVtbl = (SMH_Vtbl FAR *)&vtblSMH;
  2317.     lpsmh->lcInit = 1;
  2318.     lpsmh->hinst = hinst;
  2319.     lpsmh->lpsess = lpsess;
  2320.     lpsmh->lpfnAlloc = lpfnAlloc;
  2321.     lpsmh->lpfnAllocMore = lpfnAllocMore;
  2322.     lpsmh->lpfnFree = lpfnFree;
  2323.     memcpy (&lpsmh->muid, lpmuid, sizeof(MAPIUID));
  2324.  
  2325.     hr = HrInitSMH (lpsmh);
  2326.  
  2327. ret:
  2328.  
  2329.     if (HR_FAILED (hr))
  2330.     {
  2331.         UlRelease (lpsmh);
  2332.         lpsmh = NULL;
  2333.     }
  2334.     *lppHook = (LPSPOOLERHOOK)lpsmh;
  2335.     DebugTraceResult (SMH_Init(), hr);
  2336.     return hr;
  2337. }
  2338.