Change home directory fallback to "C:\Claws-mail" on Windows.
[claws.git] / src / common / w32_account.c
1 /* w32_account.c - Account related W32 functions.
2    Copyright (C) 2007-2009 g10 Code GmbH 
3    Copyright (C) 1999-2005 Nullsoft, Inc.
4
5    This software is provided 'as-is', without any express or implied
6    warranty. In no event will the authors be held liable for any
7    damages arising from the use of this software.
8    
9    Permission is granted to anyone to use this software for any
10    purpose, including commercial applications, and to alter it and
11    redistribute it freely, subject to the following restrictions:
12    
13    1. The origin of this software must not be misrepresented; you must
14       not claim that you wrote the original software. If you use this
15       software in a product, an acknowledgment in the product
16       documentation would be appreciated but is not required.
17    
18    2. Altered source versions must be plainly marked as such, and must
19       not be misrepresented as being the original software.
20    
21    3. This notice may not be removed or altered from any source
22       distribution.
23
24  =======[ wk 2007-05-21 ]====
25    The code for get_group_name has been taken from NSIS 2.05, module
26    UserInfo.c.  NSIS bears the above license and along with the
27    notice:
28      This license applies to everything in the NSIS package, except where
29      otherwise noted.
30    Thus we make this module available under the same license - note,
31    that this lincese is fully compatibe with the GNU GPL 2.0.
32 */ 
33   
34   
35
36
37 #include <stdlib.h>
38 #include <string.h>
39 #include <windows.h>
40 #include <sddl.h> // for ConvertSidToStringSid()
41
42 #include "w32lib.h"
43 #include "utils.h"
44
45 #ifndef DIM
46 #define DIM(v)  (sizeof(v)/sizeof((v)[0]))
47 #endif
48
49
50 /* Return a malloced name of our user group.  */
51 static char *
52 get_group_name (void)
53 {
54   HANDLE        hThread;
55   TOKEN_GROUPS  *ptg = NULL;
56   DWORD         cbTokenGroups;
57   DWORD         i, j;
58   SID_IDENTIFIER_AUTHORITY SystemSidAuthority = { SECURITY_NT_AUTHORITY };
59   char *group;
60   struct
61   {
62     DWORD auth_id;
63     char *name;
64   } groups[] = 
65     {
66       /* Every user belongs to the users group, hence
67          users comes before guests */
68       {DOMAIN_ALIAS_RID_USERS, "User"},
69       {DOMAIN_ALIAS_RID_GUESTS, "Guest"},
70       {DOMAIN_ALIAS_RID_POWER_USERS, "Power"},
71       {DOMAIN_ALIAS_RID_ADMINS, "Admin"}
72     };
73
74
75   group = NULL;
76   if (GetVersion() & 0x80000000) 
77     {
78       /* This is not NT; thus we are always Admin. */
79       group = "Admin";
80     }
81   else if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hThread) 
82            || OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hThread))
83     {
84       /* With the token for the current thread or process in hand we
85          query the size of the associated group information.  Note
86          that we expect an error because buffer has been passed as
87          NULL. cbTokenGroups will then tell use the required size.  */
88       if (!GetTokenInformation (hThread, TokenGroups, NULL, 0, &cbTokenGroups)
89           && GetLastError () == ERROR_INSUFFICIENT_BUFFER)
90         {
91           ptg = GlobalAlloc (GPTR, cbTokenGroups);
92           if (ptg)
93             {
94               if (GetTokenInformation ( hThread, TokenGroups, ptg,
95                                         cbTokenGroups, &cbTokenGroups))
96                 {
97
98                   /* Now iterate through the list of groups for this
99                      access token looking for a match against the SID
100                      we created above. */
101                   for (i = 0; i < DIM (groups); i++)
102                     {
103                       PSID psid = 0;
104                       
105                       AllocateAndInitializeSid (&SystemSidAuthority,
106                                                 2,
107                                                 SECURITY_BUILTIN_DOMAIN_RID,
108                                                 groups[i].auth_id,
109                                                 0, 0, 0, 0, 0, 0,
110                                                 &psid);
111                       if (!psid) 
112                         continue;
113                       for (j = 0; j < ptg->GroupCount; j++)
114                         if (EqualSid(ptg->Groups[j].Sid, psid))
115                           group = groups[i].name;
116                       FreeSid(psid);
117                     }
118                 }
119               
120               GlobalFree(ptg);
121             }
122         }
123       
124       CloseHandle(hThread);
125     }
126
127   return group? strdup (group):NULL;
128 }
129
130
131 /* Return true if we are an administrator.  The chekc is done only
132    once so if the current user has been hadded to the Administrator
133    group the process needs to be rerstarted. */
134 int
135 w32_is_administrator (void)
136 {
137   static int got_it;
138   static int is_admin;
139
140   if (!got_it)
141     {
142       char *name = get_group_name ();
143
144       if (name && !strcmp (name, "Admin"))
145         is_admin = 1;
146       got_it = 1;
147       free (name);
148     }
149
150   return is_admin;
151 }
152
153 /* Vomit bags are available under your seats */
154 int w32_get_uid_equiv(void)
155 {
156         HANDLE Thandle;
157         DWORD size;
158         PSID sid;
159         char *sidStr = NULL;
160         char *last_sep = NULL;
161         static int siduid = -1;
162         static BOOL (WINAPI *GetTokenInformation)(
163           HANDLE TokenHandle,
164           TOKEN_INFORMATION_CLASS TokenInformationClass,
165           LPVOID TokenInformation,
166           DWORD TokenInformationLength,
167           PDWORD ReturnLength
168         );
169         static  BOOL (WINAPI *ConvertSidToStringSid)(
170           PSID Sid,
171           LPTSTR *StringSid
172         );
173         static int TokenLogonSid = 28 /* according to Google. */;
174
175         if (siduid == -1) {
176                 void *handle = LoadLibrary ("advapi32.dll");
177                 if (handle) {
178                         GetTokenInformation = GetProcAddress (handle, "GetTokenInformation");
179                         if (!GetTokenInformation) {
180                                 FreeLibrary (handle);
181                                 debug_print("can't resolve GetTokenInformation\n");
182                                 return -1;
183                         }
184                 }
185
186                 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &Thandle) == 0
187                     && OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &Thandle) == 0) {
188                         debug_print("OpenProcessToken error %d\n", GetLastError());
189                         return -1;
190                 }
191
192                 if (GetTokenInformation(Thandle, TokenLogonSid, &sid, sizeof(sid), &size) == 0) {
193                         debug_print("GetTokenInformation error %d\n", GetLastError());
194                         return -1;
195                 }
196                 if (ConvertSidToStringSid((PSID)sid, &sidStr) == 0) {
197                         debug_print("ConvertSidToStringSid error %d\n", GetLastError());
198                         return -1;
199                 }
200
201                 debug_print("SID %s\n", sidStr);
202                 last_sep = strrchr(sidStr, '-');
203                 if (last_sep != NULL) {
204                         last_sep++;
205                         siduid = strtol(last_sep, NULL, 10);
206                         debug_print("Using UID equivalent %d\n", siduid);
207                 }
208                 LocalFree(sidStr);
209         }
210         return siduid;
211 }