Sync with trunk r58033.
[reactos.git] / base / applications / shutdown / misc.c
index d7b042b..b26ff70 100644 (file)
+/*
+ * COPYRIGHT:       See COPYING in the top level directory
+ * PROJECT:         ReactOS shutdown/logoff utility
+ * FILE:            base/applications/shutdown/misc.c
+ * PURPOSE:         Misc. functions used for the shutdown utility
+ * PROGRAMMERS:     Lee Schroeder
+ */
+
 #include "precomp.h"
 
-static INT
-LengthOfStrResource(IN HINSTANCE hInst,
-                    IN UINT uID)
+const DWORD defaultReason = SHTDN_REASON_MAJOR_OTHER | SHTDN_REASON_MINOR_OTHER;
+
+REASON shutdownReason[] =
 {
-    HRSRC hrSrc;
-    HGLOBAL hRes;
-    LPWSTR lpName, lpStr;
+    {L"U" , 0,  0, SHTDN_REASON_MAJOR_OTHER | SHTDN_REASON_MINOR_OTHER},                                             /* Other (Unplanned) */
+    {L"E" , 0,  0, SHTDN_REASON_MAJOR_OTHER | SHTDN_REASON_MINOR_OTHER},                                             /* Other (Unplanned) */
+    {L"EP", 0,  0, SHTDN_REASON_MAJOR_OTHER | SHTDN_REASON_MINOR_OTHER | SHTDN_REASON_FLAG_PLANNED},                 /* Other (Planned) */
+    {L"U" , 0,  5, SHTDN_REASON_MAJOR_OTHER | SHTDN_REASON_MINOR_HUNG},                                              /* Other Failure: System Unresponsive */
+    {L"E" , 1,  1, SHTDN_REASON_MAJOR_HARDWARE | SHTDN_REASON_MINOR_MAINTENANCE},                                    /* Hardware: Maintenance (Unplanned) */
+    {L"EP", 1,  1, SHTDN_REASON_MAJOR_HARDWARE | SHTDN_REASON_MINOR_MAINTENANCE | SHTDN_REASON_FLAG_PLANNED},        /* Hardware: Maintenance (Planned) */
+    {L"E" , 1,  2, SHTDN_REASON_MAJOR_HARDWARE | SHTDN_REASON_MINOR_INSTALLATION},                                   /* Hardware: Installation (Unplanned) */
+    {L"EP", 1,  2, SHTDN_REASON_MAJOR_HARDWARE | SHTDN_REASON_MINOR_INSTALLATION | SHTDN_REASON_FLAG_PLANNED},       /* Hardware: Installation (Planned) */
+    {L"P" , 2,  3, SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_UPGRADE | SHTDN_REASON_FLAG_PLANNED},     /* Operating System: Upgrade (Planned) */
+    {L"E" , 2,  4, SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_RECONFIG},                                /* Operating System: Reconfiguration (Unplanned) */
+    {L"EP", 2,  4, SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_RECONFIG | SHTDN_REASON_FLAG_PLANNED},    /* Operating System: Reconfiguration (Planned) */
+    {L"P" , 2, 16, SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_RECONFIG | SHTDN_REASON_FLAG_PLANNED},    /* Operating System: Service pack (Planned) */
+    {L"U" , 2, 17, SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_HOTFIX},                                  /* Operating System: Hotfix (Unplanned) */
+    {L"P" , 2, 17, SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_HOTFIX | SHTDN_REASON_FLAG_PLANNED},      /* Operating System: Hotfix (Planned) */
+    {L"U" , 2, 18, SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_SECURITYFIX},                             /* Operating System: Security fix (Unplanned) */
+    {L"P" , 2, 18, SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_SECURITYFIX | SHTDN_REASON_FLAG_PLANNED}, /* Operating System: Security fix (Planned) */
+    {L"E" , 4,  1, SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_MAINTENANCE},                                 /* Application: Maintenance (Unplanned) */
+    {L"EP", 4,  1, SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_MAINTENANCE | SHTDN_REASON_FLAG_PLANNED},     /* Application: Maintenance (Planned) */
+    {L"EP", 4,  2, SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_INSTALLATION | SHTDN_REASON_FLAG_PLANNED},    /* Application: Installation (Planned) */
+    {L"E" , 4,  5, SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_HUNG},                                        /* Application: Unresponsive */
+    {L"E" , 4,  6, SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_UNSTABLE},                                    /* Application: Unstable */
+    {L"U" , 5, 15, SHTDN_REASON_MAJOR_SYSTEM | SHTDN_REASON_MINOR_BLUESCREEN},                                       /* System Failure: Stop Error */
+    {L"E" , 5, 19, SHTDN_REASON_MAJOR_SYSTEM | SHTDN_REASON_MINOR_SECURITY},                                         /* Security Issue */
+    {L"U" , 5, 19, SHTDN_REASON_MAJOR_SYSTEM | SHTDN_REASON_MINOR_SECURITY},                                         /* Security Issue */
+    {L"EP", 5, 19, SHTDN_REASON_MAJOR_SYSTEM | SHTDN_REASON_MINOR_SECURITY | SHTDN_REASON_FLAG_PLANNED},             /* Security Issue (Planned) */
+    {L"E" , 5, 20, SHTDN_REASON_MAJOR_SYSTEM | SHTDN_REASON_MINOR_NETWORK_CONNECTIVITY},                             /* Loss of Network Connectivity (Unplanned) */
+    {L"U" , 6, 11, SHTDN_REASON_MAJOR_POWER | SHTDN_REASON_MINOR_CORDUNPLUGGED},                                     /* Power Failure: Cord Unplugged */
+    {L"U" , 6, 12, SHTDN_REASON_MAJOR_POWER | SHTDN_REASON_MINOR_ENVIRONMENT},                                       /* Power Failure: Environment */
+    {L"P" , 7,  0, SHTDN_REASON_MAJOR_POWER | SHTDN_REASON_MINOR_ENVIRONMENT}                                        /* Legacy API shutdown (Planned) */
+};
 
-    if (hInst == NULL)
-    {
-        return -1;
-    }
+/*
+ * This command helps to work around the fact that the shutdown utility has
+ * different upper limits for the comment flag since each version of Windows
+ * seems to have different upper limits.
+ */
+BOOL CheckCommentLength(LPCWSTR comment)
+{
+    DWORD finalLength = 0;
+    size_t strLength = 0;
+    DWORD osVersion = 0;
+    DWORD osMajorVersion = 0;
+    DWORD osMinorVersion = 0;
 
-    /* There are always blocks of 16 strings */
-    lpName = (LPWSTR)MAKEINTRESOURCE((uID >> 4) + 1);
+    /* An empty string is always valid. */
+    if (!comment || *comment == 0)
+        return TRUE;
 
-    /* Find the string table block */
-    if ((hrSrc = FindResourceW(hInst, lpName, (LPWSTR)RT_STRING)) &&
-        (hRes = LoadResource(hInst, hrSrc)) &&
-        (lpStr = (WCHAR*) LockResource(hRes)))
-    {
-        UINT x;
+    /* Grab the version of the current Operating System. */
+    osVersion = GetVersion();
+
+    osMajorVersion = (DWORD)(LOBYTE(LOWORD(osVersion)));
+    osMinorVersion = (DWORD)(HIBYTE(LOWORD(osVersion)));
 
-        /* Find the string we're looking for */
-        uID &= 0xF; /* position in the block, same as % 16 */
-        for (x = 0; x < uID; x++)
+    /*
+     * Check to make sure that the proper length is being used
+     * based upon the version of Windows currently being used.
+     */
+    if (osMajorVersion == 5) /* Windows XP/2003 */
+    {
+        if ((osMinorVersion == 1) || (osMinorVersion == 2))
         {
-            lpStr += (*lpStr) + 1;
+            finalLength = 127;
         }
-
-        /* Found the string */
-        return (int)(*lpStr);
     }
-    return -1;
+    else if (osMajorVersion == 6) /* Windows Vista/7/2008 */
+    {
+        if ((osMinorVersion == 0) || (osMinorVersion == 1))
+        {
+            finalLength = 512;
+        }
+    }
+
+    /* Grab the length of the comment string. */
+    strLength = wcslen(comment);
+
+    /*
+     * Compare the size of the string to make sure
+     * it fits with the current version of Windows,
+     * and return TRUE or FALSE accordingly.
+     */
+    return (strLength <= finalLength);
 }
 
-INT
-AllocAndLoadString(OUT LPTSTR *lpTarget,
-                   IN HINSTANCE hInst,
-                   IN UINT uID)
+/*
+ * This function parses the reason code to a usable format that will specify
+ * why the user wants to shut the computer down. Although this is used for
+ * both client and server environments, use of a reason code is more important
+ * in a server environment since servers are supposed to be on all the time
+ * for easier access.
+ */
+DWORD ParseReasonCode(LPCWSTR code)
 {
-    INT ln;
+    PREASON reasonptr;
+    int majorCode = 0;
+    int minorCode = 0;
+    LPWSTR tmpPrefix = NULL;
+    size_t codeSize;
 
-    ln = LengthOfStrResource(hInst,
-                             uID);
-    if (ln++ > 0)
+    /* If no reason code is specified, use "Other (Unplanned)" as the default option */
+    if(code == NULL)
     {
-        (*lpTarget) = (LPTSTR)LocalAlloc(LMEM_FIXED,
-                                         ln * sizeof(TCHAR));
-        if ((*lpTarget) != NULL)
+        return defaultReason;
+    }
+    else
+    {
+        /* Store the size of the code so we can use it later */
+        codeSize = (size_t)wcslen(code);
+
+        /* A colon cannot be the first or last character in the reason code */
+        if ((code[0] == L':') || (code[codeSize] == L':'))
         {
-            INT Ret;
-            if (!(Ret = LoadString(hInst, uID, *lpTarget, ln)))
+            return defaultReason;
+        }
+
+        /* The minimum length that a reason code can be is 5-7 characters in length */
+        if ((codeSize < 5) || (codeSize > 7))
+        {
+            return defaultReason;
+        }
+
+        /* TODO: Add code for reason parsing here. */
+
+        /* Make sure that the major and minor codes are within size limits */
+        if ((majorCode > 7 ) || (majorCode < 0) ||
+            (minorCode > 20) || (minorCode < 0))
+        {
+            return defaultReason;
+        }
+
+        /* Figure out what flags to return */
+        for (reasonptr = shutdownReason ; reasonptr->prefix ; reasonptr++)
+        {
+            if ((majorCode == reasonptr->major) &&
+                (minorCode == reasonptr->minor) &&
+                (_wcsicmp(tmpPrefix, reasonptr->prefix) != 0))
             {
-                LocalFree((HLOCAL)(*lpTarget));
+                return reasonptr->flag;
             }
-            return Ret;
         }
     }
-    return 0;
+
+    return defaultReason;
 }
+
+/* Writes the last error as both text and error code to the console */
+VOID DisplayError(DWORD dwError)
+{
+    LPWSTR lpMsgBuf = NULL;
+    DWORD  errLength; /* Error message length */
+    LPSTR resMsg;     /* For error message in OEM symbols */
+
+    /* Grabs the length of the error message */
+    errLength = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+                               NULL,
+                               dwError,
+                               LANG_USER_DEFAULT,
+                               (LPWSTR)&lpMsgBuf,
+                               0,
+                               NULL) + 1;
+
+    /* Gets the error message ready for output */
+    resMsg = (LPSTR)LocalAlloc(LPTR, errLength * sizeof(WCHAR));
+    CharToOemBuffW(lpMsgBuf, resMsg, errLength);
+
+    /* Prints out the error message to the user */
+    fprintf(stderr, resMsg);
+    fwprintf(stderr, L"Error code: %d\n", dwError);
+
+    LocalFree(lpMsgBuf);
+    LocalFree(resMsg);
+}
+
+/* EOF */