free web page counters

Windows Mobile Pocket PC Smartphone Programming

==>Click here for the SiteMap<==. Original contents with decent amount of source codes.

Wednesday, April 26, 2006

Pocket Outlook Woe 02: Domain name is truncated to the same size as password after calling IMailSyncCallBack :: RequestCredentials

====>SiteMap of this Blog<===

Pocket Outlook Woe 02: Domain name is truncated to the same size as password after calling IMailSyncCallBack::RequestCredentials

In "Pocket Outlook Woe 01", I talked about one issue regarding the latest version of Pocket Outlook (installed with Windows Mobile 5.0 devices, and newer version of Pocket PC 2003 SE devices), that Pocket Outlook passes two NULL pointers as incoming mail server and outgoing mail server to a customized Transport. Today I am focusing on the second woe: domain name is truncated to the same size as that of password after IMailSyncCallBack::RequestCredentials is returned.

IMailSyncCallback: Pocket Outlook itself

The method involved here is called IMailSyncCallBack::RequestCredentials. IMailSyncCallback can be understood as Outlook itself, which provides Transport with methods to interact with Outlook. For example, Transport can:

  • request memory in tmail.exe's address space
  • request Outlook to synchronize an account
  • request Outlook to display a message box

The method giving headache here is RequestCredentials, which is used by Transport to request new credentials from the user.

HRESULT RequestCredentials ( LPCWSTR pszProfile, SYNCCREDENTIALS * ppcredsSource, SYNCCREDENTIALS ** ppcreds );

This is a blocking call. Once called, Outlook shows a dialog to the user asking username, password and domain name. Results won't be returned to Transport until after the user clicks "OK" or "Cancel" in the dialog.

Domain name is truncated!

This simple API works greatly in previous version of Pocket Outlook. However, in the newer version, domain name is always truncated to the same size as that of password. Below are some screen cuts with illustrations:

Screen 1: RequestCredentials() is called. Domain name is "aa.com" (6 characters), password is "12345"

Screen 2: RequestCredentials() returns with a new credential (in parameter ppcreds). Notice domain name is truncated to "aa.co".

Screen 3: RequestCredentials() is called again. Now parameter ppcreds is copied to ppcredsSource.

Screen 4: Let us revert the domain name back to "aa.com", but type "123" as the password.

Screen 5: RequestCredentials() returns with another new credential. Now the domain name is truncated to "aa."

Source code used

The source code is adapted from TransportDemo, really seemingly simple and innocent code.

HRESULT CTransportSyncHandler::Connect(
   DWORD dwReserved,
   SYNCCREDENTIALS* pCredentials
)
{
   HRESULT                            hr                               =      S_OK;
   SYNCCREDENTIALS *      pCurrentCreds      =      pCredentials;
   ULONG                                 cTries                      =      0;

   // The transport is no longer shut down
   ASSERT(m_hEventShutdown != NULL);
   m_fShutDown = FALSE;
   ResetEvent(m_hEventShutdown);

   //while(TRUE)
   while(cTries <= 4) {
       // Do we need to prompt for account info?
       if(cTries || *pCurrentCreds->pszPassword == L'\0') {
            SYNCCREDENTIALS * pNewCreds = NULL;
         
            // Make a request to the Inbox for logon credentials.
            hr = m_pCallback->RequestCredentials(m_pszProfile, pCurrentCreds, &pNewCreds);
            if(FAILED(hr)) {
                // Log an error...
                if (!m_fShutDown) {
                     LogErrorEvent(CEMAPI_E_NO_ACCOUNT_INFO, 0);
                }
                break;
            }
         
            // pNewCreds now contains the new info from the user.
            ASSERT(pNewCreds != NULL);
         
            // Check to see if 'pCurrentCreds' is *not* the ones passed in.
            // If they are not, then we need to free them.    This will happen
            // if the user has been asked more than once for the password.
            if(pCurrentCreds != pCredentials) {
                m_pCallback->FreeMem(pCurrentCreds);
            }

            // what is the new credential
            {
                TCHAR szInfo[1024];
                swprintf(szInfo, TEXT("After RequestCredentials>>\nUserName: %s\nPassword: %s\nDomain: %s"), pNewCreds->pszUsername, pNewCreds->pszPassword, pNewCreds->pszDomain);
                ALERT(szInfo);
            }
         
            pCurrentCreds = pNewCreds;
       }
   
       // Now try to connect to the mail server
       // [ Omitted ]
       hr = E_ACCESSDENIED;
   
       // [ Assumed to return hr ]
       if(FAILED(hr)) {
            if (!m_fShutDown) {
                LogErrorEvent(hr, 0);
            }
         
            //break;
            // retry instead
       }
   
       if(m_fShutDown) {
            hr = E_FAIL;
            break;
       }
   
       // Log in to the mail server
       SetStatusText(0/*IDS_TRANSPORT_CONNECTING*/);          // TODO:    Use an appropriate string resource
       // [ Omitted ]
       // [ Assumed to return hr ]
   
       // If the logon succeeded, we're done
       if(SUCCEEDED(hr)) {
            break;
       }
   
       // If we failed due to anything but authentication errors,
       // log the error and bail out
       if(hr != E_ACCESSDENIED) {
            // Log an error back to the application if user didn't himself ask for shutdown
            if (!m_fShutDown) {
                LogErrorEvent(hr, 0);
            }
            break;
       }
   
       // No success!    Need to hang up and ask the user for new
       // credentials.
       ++cTries;

                // Disconnect from the mail server
       // [ Omitted ]
       // [ Assumed to return hr ]
   }      // while(TRUE)

   if(FAILED(hr)) {
       // Disconnect from the mail server
       // [ Omitted ]
       // [ Assumed to return hr ]
   }

   return hr;
}

Wrap Up

Similar to Outlook Woe 1, I tried to disable the device security, or sign the transport DLL using a certificate, but the problem persists.

This seems affecting not only Windows Mobile 5.0 device, but also newer version of Pocket PC 2003 SE device, where the newer version of Outlook is shipped. The old version of Pocket Outlook actually does NOT show domain-name input box in the credential request dialog.

As a workaround, the user might be required to have a password with the size not shorter than the domain name. For example, "microsoft.com" employee must have a password with at least 13 characters. This sounds like a ridiculous requirement. So again the burden lies on the shoulder of transport developers, who might remember the "original" domain name in a place, and ignore the one passed by IMailSyncCallback. Hereby "original" domain name refers to the one passed by IMailSyncCallback the first time RequestCredentials() is called. Alternatively, registry can be used to store the domain information, rather than relying on Outlook. However, this means that the user cannot change the domain settings via Outlook.

Also similar to Outlook Woe 1, the POP3 transport and IMAP4 transport shipped along with Pocket Outlook seem immune to the issue. Could it because the TransportDemo code is not doing the correct thing in its Connect() method?

====>SiteMap of this Blog<===