Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

805 lines
27 KiB

  1. // --------------------------------------------------------------------------
  2. // Module Name: UserPict.cpp
  3. //
  4. // Copyright (c) 2000, Microsoft Corporation
  5. //
  6. // Functions that implement user picture manipulation.
  7. //
  8. // History: 2000-03-24 vtan created
  9. // 2000-05-03 jeffreys reworked using DIB sections
  10. // 2000-10-26 jeffreys switched from %ALLUSERSPROFILE%\Pictures
  11. // to CSIDL_COMMON_APPDATA\User Account Pictures
  12. // --------------------------------------------------------------------------
  13. #include "shellprv.h"
  14. #include <lmcons.h>
  15. #include <shimgdata.h>
  16. #include <aclapi.h> // for SetNamedSecurityInfo
  17. #include <shgina.h> // for ILogonUser
  18. #include <strsafe.h>
  19. #pragma warning(push,4)
  20. // --------------------------------------------------------------------------
  21. // SaveDIBSectionToFile
  22. //
  23. // Arguments: hbm = Source image (DIB section) to save
  24. // hdc = Device Context containing hbm. May be NULL
  25. // if hbm is not selected in any DC or hbm
  26. // is known to have no color table.
  27. // pszFile = Target image file.
  28. //
  29. // Returns: BOOL
  30. //
  31. // Purpose: Write a DIB to disk in the proper format
  32. //
  33. // History: 2000-05-03 jeffreys created
  34. // --------------------------------------------------------------------------
  35. #define DIB_HEADER_MARKER ((WORD) ('M' << 8) | 'B')
  36. // SECURITY: caller is responsible for pszFile being valid
  37. BOOL SaveDIBSectionToFile(HBITMAP hbm, HDC hdc, LPCTSTR pszFile)
  38. {
  39. BOOL bResult;
  40. DIBSECTION ds;
  41. HANDLE hFile;
  42. BITMAPFILEHEADER bf;
  43. DWORD cbWritten;
  44. bResult = FALSE;
  45. // Get the details about the bitmap. This also validates hbm.
  46. if (GetObject(hbm, sizeof(ds), &ds) == 0)
  47. return FALSE;
  48. // Fill in a couple of optional fields if necessary
  49. if (ds.dsBmih.biSizeImage == 0)
  50. ds.dsBmih.biSizeImage = ds.dsBmih.biHeight * ds.dsBm.bmWidthBytes;
  51. if (ds.dsBmih.biBitCount <= 8 && ds.dsBmih.biClrUsed == 0)
  52. ds.dsBmih.biClrUsed = 1 << ds.dsBmih.biBitCount;
  53. // Open the target file. This also validates pszFile.
  54. hFile = CreateFile(pszFile,
  55. GENERIC_WRITE,
  56. 0,
  57. NULL,
  58. CREATE_ALWAYS,
  59. FILE_ATTRIBUTE_NORMAL,
  60. NULL);
  61. if (INVALID_HANDLE_VALUE == hFile)
  62. return FALSE;
  63. // Prepare the BITMAPFILEHEADER for writing
  64. bf.bfType = DIB_HEADER_MARKER;
  65. bf.bfReserved1 = 0;
  66. bf.bfReserved2 = 0;
  67. // The bit offset is the cumulative size of all of the header stuff
  68. bf.bfOffBits = sizeof(bf) + sizeof(ds.dsBmih) + (ds.dsBmih.biClrUsed*sizeof(RGBQUAD));
  69. if (ds.dsBmih.biCompression == BI_BITFIELDS)
  70. bf.bfOffBits += sizeof(ds.dsBitfields);
  71. // Round up to the next 16-byte boundary. This isn't strictly necessary,
  72. // but it makes the file layout cleaner. (You can create a file mapping
  73. // and pass it to CreateDIBSection this way.)
  74. bf.bfOffBits = ((bf.bfOffBits + 15) & ~15);
  75. // The file size is the bit offset + the size of the bits
  76. bf.bfSize = bf.bfOffBits + ds.dsBmih.biSizeImage;
  77. // Write the BITMAPFILEHEADER first
  78. bResult = WriteFile(hFile, &bf, sizeof(bf), &cbWritten, NULL);
  79. if (bResult)
  80. {
  81. // Next is the BITMAPINFOHEADER
  82. bResult = WriteFile(hFile, &ds.dsBmih, sizeof(ds.dsBmih), &cbWritten, NULL);
  83. if (bResult)
  84. {
  85. // Then the 3 bitfields, if necessary
  86. if (ds.dsBmih.biCompression == BI_BITFIELDS)
  87. {
  88. bResult = WriteFile(hFile, &ds.dsBitfields, sizeof(ds.dsBitfields), &cbWritten, NULL);
  89. }
  90. if (bResult)
  91. {
  92. // Now the color table, if any
  93. if (ds.dsBmih.biClrUsed != 0)
  94. {
  95. RGBQUAD argb[256];
  96. HDC hdcDelete;
  97. HBITMAP hbmOld;
  98. // Assume failure here
  99. bResult = FALSE;
  100. hdcDelete = NULL;
  101. if (!hdc)
  102. {
  103. hdcDelete = CreateCompatibleDC(NULL);
  104. if (hdcDelete)
  105. {
  106. hbmOld = (HBITMAP)SelectObject(hdcDelete, hbm);
  107. hdc = hdcDelete;
  108. }
  109. }
  110. if (hdc &&
  111. GetDIBColorTable(hdc, 0, ARRAYSIZE(argb), argb) == ds.dsBmih.biClrUsed)
  112. {
  113. bResult = WriteFile(hFile, argb, ds.dsBmih.biClrUsed*sizeof(RGBQUAD), &cbWritten, NULL);
  114. }
  115. if (hdcDelete)
  116. {
  117. SelectObject(hdcDelete, hbmOld);
  118. DeleteDC(hdcDelete);
  119. }
  120. }
  121. // Finally, write the bits
  122. if (bResult)
  123. {
  124. SetFilePointer(hFile, bf.bfOffBits, NULL, FILE_BEGIN);
  125. bResult = WriteFile(hFile, ds.dsBm.bmBits, ds.dsBmih.biSizeImage, &cbWritten, NULL);
  126. SetEndOfFile(hFile);
  127. }
  128. }
  129. }
  130. }
  131. CloseHandle(hFile);
  132. if (!bResult)
  133. {
  134. // Something failed, clean up
  135. DeleteFile(pszFile);
  136. }
  137. return bResult;
  138. }
  139. // --------------------------------------------------------------------------
  140. // MakeDIBSection
  141. //
  142. // Arguments: pImage = Source image
  143. //
  144. // Returns: HBITMAP
  145. //
  146. // Purpose: Create a DIB section containing the given image
  147. // on a white background
  148. //
  149. // History: 2000-05-03 jeffreys created
  150. // --------------------------------------------------------------------------
  151. HBITMAP MakeDIBSection(IShellImageData *pImage, ULONG cx, ULONG cy)
  152. {
  153. HBITMAP hbm;
  154. HDC hdc;
  155. BITMAPINFO dib;
  156. hdc = CreateCompatibleDC(NULL);
  157. if (hdc == NULL)
  158. return NULL;
  159. dib.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  160. dib.bmiHeader.biWidth = cx;
  161. dib.bmiHeader.biHeight = cy;
  162. dib.bmiHeader.biPlanes = 1;
  163. dib.bmiHeader.biBitCount = 24;
  164. dib.bmiHeader.biCompression = BI_RGB;
  165. dib.bmiHeader.biSizeImage = 0;
  166. dib.bmiHeader.biXPelsPerMeter = 0;
  167. dib.bmiHeader.biYPelsPerMeter = 0;
  168. dib.bmiHeader.biClrUsed = 0;
  169. dib.bmiHeader.biClrImportant = 0;
  170. hbm = CreateDIBSection(hdc, &dib, DIB_RGB_COLORS, NULL, NULL, 0);
  171. if (hbm)
  172. {
  173. HBITMAP hbmOld;
  174. RECT rc;
  175. hbmOld = (HBITMAP)SelectObject(hdc, hbm);
  176. // Initialize the entire image with white
  177. PatBlt(hdc, 0, 0, cx, cy, WHITENESS);
  178. rc.left = 0;
  179. rc.top = 0;
  180. rc.right = cx;
  181. rc.bottom = cy;
  182. // Draw the source image into the DIB section
  183. HRESULT hr = pImage->Draw(hdc, &rc, NULL);
  184. SelectObject(hdc, hbmOld);
  185. if (FAILED(hr))
  186. {
  187. DeleteObject(hbm);
  188. hbm = NULL;
  189. SetLastError(hr);
  190. }
  191. }
  192. DeleteDC(hdc);
  193. return hbm;
  194. }
  195. // --------------------------------------------------------------------------
  196. // ConvertAndResizeImage
  197. //
  198. // Arguments: pszFileSource = Source image file.
  199. // pszFileTarget = Target image file (resized).
  200. //
  201. // Returns: HRESULT
  202. //
  203. // Purpose: Uses GDI+ via COM interfaces to convert the given image file
  204. // to a bmp sized at 96x96.
  205. //
  206. // History: 2000-03-24 vtan created
  207. // 2000-05-03 jeffreys reworked using DIB sections
  208. // --------------------------------------------------------------------------
  209. HRESULT ConvertAndResizeImage (LPCTSTR pszFileSource, LPCTSTR pszFileTarget)
  210. {
  211. HRESULT hr;
  212. IShellImageDataFactory *pImagingFactory;
  213. hr = CoCreateInstance(CLSID_ShellImageDataFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellImageDataFactory, &pImagingFactory));
  214. if (SUCCEEDED(hr))
  215. {
  216. IShellImageData *pImage;
  217. hr = pImagingFactory->CreateImageFromFile(pszFileSource, &pImage);
  218. if (SUCCEEDED(hr))
  219. {
  220. hr = pImage->Decode(SHIMGDEC_DEFAULT, 0, 0);
  221. if (SUCCEEDED(hr))
  222. {
  223. SIZE sizeImg;
  224. ULONG cxDest, cyDest;
  225. HDC hdc;
  226. HBITMAP hbm;
  227. DWORD dwErr;
  228. // The default dimensions are based on the screen resolution
  229. hdc = GetDC(NULL);
  230. if (hdc != NULL)
  231. {
  232. // Make it 1/2 inch square by default
  233. cxDest = GetDeviceCaps(hdc, LOGPIXELSX) / 2;
  234. cyDest = GetDeviceCaps(hdc, LOGPIXELSY) / 2;
  235. ReleaseDC(NULL, hdc);
  236. }
  237. else
  238. {
  239. // Most common display modes run at 96dpi ("small fonts")
  240. cxDest = cyDest = 48;
  241. }
  242. // Get the current image dimensions so we can maintain aspect ratio
  243. if ( SUCCEEDED(pImage->GetSize(&sizeImg)) )
  244. {
  245. // Don't want to make small images bigger
  246. cxDest = min(cxDest, (ULONG)sizeImg.cx);
  247. cyDest = min(cyDest, (ULONG)sizeImg.cy);
  248. // If it's not square, scale the smaller dimension
  249. // to maintain the aspect ratio.
  250. if (sizeImg.cx > sizeImg.cy)
  251. {
  252. cyDest = MulDiv(cxDest, sizeImg.cy, sizeImg.cx);
  253. }
  254. else if (sizeImg.cx < sizeImg.cy)
  255. {
  256. cxDest = MulDiv(cyDest, sizeImg.cx, sizeImg.cy);
  257. }
  258. }
  259. // Resize the image
  260. // Note that this gives better results than scaling while drawing
  261. // into the DIB section (see MakeDIBSection).
  262. //
  263. // However, it doesn't always work. For example, animated images
  264. // result in E_NOTVALIDFORANIMATEDIMAGE. So ignore the return
  265. // value and the scaling will be done in MakeDIBSection if necessary.
  266. pImage->Scale(cxDest, cyDest, 0);
  267. hbm = MakeDIBSection(pImage, cxDest, cyDest);
  268. if (hbm)
  269. {
  270. // Save the DIB section to disk
  271. if (!SaveDIBSectionToFile(hbm, NULL, pszFileTarget))
  272. {
  273. dwErr = GetLastError();
  274. hr = HRESULT_FROM_WIN32(dwErr);
  275. }
  276. DeleteObject(hbm);
  277. }
  278. else
  279. {
  280. dwErr = GetLastError();
  281. hr = HRESULT_FROM_WIN32(dwErr);
  282. }
  283. }
  284. pImage->Release();
  285. }
  286. pImagingFactory->Release();
  287. }
  288. return(hr);
  289. }
  290. // --------------------------------------------------------------------------
  291. // SetExplicitAccessToObject
  292. //
  293. // Arguments: pszTarget = Target object
  294. // seType = Type of object
  295. // pszUser = User to grant access to
  296. // dwMask = Permissions granted
  297. // dwFlags = Inheritance flags
  298. //
  299. // Returns: BOOL
  300. //
  301. // Purpose: Grants Read/Write/Execute/Delete access to the
  302. // specified user on the specified file.
  303. //
  304. // Note that this stomps existing explicit entries in the DACL.
  305. // Multiple calls are not cumulative.
  306. //
  307. // History: 2000-05-19 jeffreys created
  308. // --------------------------------------------------------------------------
  309. DWORD SetExplicitAccessToObject(LPTSTR pszTarget, SE_OBJECT_TYPE seType, LPCTSTR pszUser, DWORD dwMask, DWORD dwFlags)
  310. {
  311. BOOL bResult;
  312. // 84 bytes
  313. BYTE rgAclBuffer[sizeof(ACL)
  314. + (sizeof(ACCESS_ALLOWED_ACE) - sizeof(ULONG))
  315. + (sizeof(SID) + (SID_MAX_SUB_AUTHORITIES-1)*sizeof(ULONG))];
  316. PACL pDacl = (PACL)rgAclBuffer;
  317. if (!InitializeAcl(pDacl, sizeof(rgAclBuffer), ACL_REVISION)) return FALSE;
  318. pDacl->AceCount = 1;
  319. PACCESS_ALLOWED_ACE pAce = (PACCESS_ALLOWED_ACE)(pDacl+1);
  320. pAce->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
  321. pAce->Header.AceFlags = (UCHAR)(dwFlags & 0xFF);
  322. pAce->Mask = dwMask;
  323. SID_NAME_USE snu;
  324. TCHAR szDomainName[MAX_PATH];
  325. DWORD cbDomainName = ARRAYSIZE(szDomainName);
  326. DWORD cbSid = sizeof(SID) + (SID_MAX_SUB_AUTHORITIES-1)*sizeof(ULONG);
  327. bResult = LookupAccountName(
  328. NULL,
  329. pszUser,
  330. (PSID)&(pAce->SidStart),
  331. &cbSid,
  332. szDomainName,
  333. &cbDomainName,
  334. &snu);
  335. if (bResult)
  336. {
  337. DWORD dwErr;
  338. // LookupAccountName doesn't return the SID length on success
  339. cbSid = GetLengthSid((PSID)&(pAce->SidStart));
  340. // Update the ACE size
  341. pAce->Header.AceSize = (USHORT)(sizeof(ACCESS_ALLOWED_ACE) - sizeof(ULONG) + cbSid);
  342. dwErr = SetNamedSecurityInfo(
  343. pszTarget,
  344. seType,
  345. DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION,
  346. NULL,
  347. NULL,
  348. pDacl,
  349. NULL);
  350. if (ERROR_SUCCESS != dwErr)
  351. {
  352. SetLastError(dwErr);
  353. bResult = FALSE;
  354. }
  355. }
  356. return bResult;
  357. }
  358. // --------------------------------------------------------------------------
  359. // SetDefaultUserPicture
  360. //
  361. // Arguments: pszUsername = Desired user (NULL for current user).
  362. //
  363. // Returns: HRESULT
  364. //
  365. // Purpose: Picks one of the default user pictures at random and
  366. // assigns it to the specified user.
  367. //
  368. // History: 2001-03-27 reinerf created
  369. // --------------------------------------------------------------------------
  370. HRESULT SetDefaultUserPicture(LPCTSTR pszUsername)
  371. {
  372. HRESULT hr;
  373. TCHAR szPath[MAX_PATH];
  374. hr = SHGetUserPicturePath(NULL, SHGUPP_FLAG_DEFAULTPICSPATH, szPath);
  375. if (SUCCEEDED(hr))
  376. {
  377. BOOL bFound = FALSE; // assume we won't find a picture
  378. // Assume everything in the dir is a vaild image file
  379. if (PathAppend(szPath, TEXT("*.*")))
  380. {
  381. static DWORD dwSeed = 0;
  382. WIN32_FIND_DATA fd;
  383. HANDLE hFind = FindFirstFile(szPath, &fd);
  384. if (dwSeed == 0)
  385. {
  386. dwSeed = GetTickCount();
  387. }
  388. if (hFind != INVALID_HANDLE_VALUE)
  389. {
  390. DWORD dwCount = 0;
  391. // use a probability collector algorithim (with a limit of 100 files)
  392. do
  393. {
  394. if (!PathIsDotOrDotDot(fd.cFileName))
  395. {
  396. dwCount++;
  397. // although RtlRandom returns a ULONG it is distributed from 0...MAXLONG
  398. if (RtlRandomEx(&dwSeed) <= (MAXLONG / dwCount))
  399. {
  400. bFound = TRUE;
  401. PathRemoveFileSpec(szPath);
  402. PathAppend(szPath, fd.cFileName);
  403. }
  404. }
  405. } while (FindNextFile(hFind, &fd) && (dwCount < 100));
  406. FindClose(hFind);
  407. }
  408. }
  409. if (bFound)
  410. {
  411. hr = SHSetUserPicturePath(pszUsername, 0, szPath);
  412. }
  413. else
  414. {
  415. hr = E_FAIL;
  416. }
  417. }
  418. return hr;
  419. }
  420. // --------------------------------------------------------------------------
  421. // ::SHGetUserPicturePath
  422. //
  423. // Arguments: pszUsername = Desired user (NULL for current user).
  424. // dwFlags = Flags.
  425. // pszPath = Path to user picture.
  426. //
  427. // Returns: HRESULT
  428. //
  429. // Purpose: Returns the user's picture path (absolute). Does parameter
  430. // validation as well. This function only supports .bmp files.
  431. //
  432. // Use SHGUPP_FLAG_BASEPATH to return the base to the pictures
  433. // directory.
  434. //
  435. // Use SHGUPP_FLAG_DEFAULTPICSPATH to return the path to the
  436. // default pictures directory.
  437. //
  438. // Use SHGUPP_FLAG_CREATE to create the user picture directory.
  439. //
  440. // If neither SHGUPP_FLAG_BASEPATH or SHGUPP_FLAG_DEFAULTPICSPATH
  441. // is specified, and the user has no picture, SHGUPP_FLAG_CREATE
  442. // will select one of the default pictures at random.
  443. //
  444. // History: 2000-02-22 vtan created
  445. // 2000-03-24 vtan moved from folder.cpp
  446. // --------------------------------------------------------------------------
  447. #define UASTR_PATH_PICTURES TEXT("Microsoft\\User Account Pictures")
  448. #define UASTR_PATH_DEFPICS UASTR_PATH_PICTURES TEXT("\\Default Pictures")
  449. // SECURITY: Assumes pszPath is MAX_PATH
  450. STDAPI SHGetUserPicturePath (LPCTSTR pszUsername, DWORD dwFlags, LPTSTR pszPath)
  451. {
  452. HRESULT hr;
  453. TCHAR szPath[MAX_PATH];
  454. // Validate dwFlags.
  455. if ((dwFlags & SHGUPP_FLAG_INVALID_MASK) != 0)
  456. {
  457. return(E_INVALIDARG);
  458. }
  459. // Validate pszPath. This must not be NULL.
  460. if (pszPath == NULL)
  461. {
  462. return(E_INVALIDARG);
  463. }
  464. // Start by getting the base picture path
  465. hr = SHGetFolderPathAndSubDir(NULL,
  466. (dwFlags & SHGUPP_FLAG_CREATE) ? (CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE) : CSIDL_COMMON_APPDATA,
  467. NULL,
  468. SHGFP_TYPE_CURRENT,
  469. (dwFlags & SHGUPP_FLAG_DEFAULTPICSPATH) ? UASTR_PATH_DEFPICS : UASTR_PATH_PICTURES,
  470. szPath);
  471. // If the base path is requested this function is done.
  472. if (S_OK == hr && 0 == (dwFlags & (SHGUPP_FLAG_BASEPATH | SHGUPP_FLAG_DEFAULTPICSPATH)))
  473. {
  474. TCHAR szUsername[UNLEN + sizeof('\0')];
  475. if (pszUsername == NULL)
  476. {
  477. DWORD dwUsernameSize;
  478. dwUsernameSize = ARRAYSIZE(szUsername);
  479. if (GetUserName(szUsername, &dwUsernameSize) != FALSE)
  480. {
  481. pszUsername = szUsername;
  482. }
  483. else
  484. {
  485. hr = HRESULT_FROM_WIN32(GetLastError());
  486. }
  487. }
  488. if (pszUsername != NULL)
  489. {
  490. // Append the user name to the picture path. Then look for
  491. // <username>.bmp. This function only supports bmp.
  492. PathAppend(szPath, pszUsername);
  493. lstrcatn(szPath, TEXT(".bmp"), ARRAYSIZE(szPath));
  494. if (PathFileExistsAndAttributes(szPath, NULL) != FALSE)
  495. {
  496. hr = S_OK;
  497. }
  498. else if (dwFlags & SHGUPP_FLAG_CREATE)
  499. {
  500. // No picture has been set for this user. Select one
  501. // of the default pictures at random.
  502. hr = SetDefaultUserPicture(pszUsername);
  503. ASSERT(FAILED(hr) || PathFileExistsAndAttributes(szPath, NULL));
  504. }
  505. else
  506. {
  507. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  508. }
  509. }
  510. }
  511. if (S_OK == hr)
  512. {
  513. hr = StringCchCopy(pszPath, MAX_PATH, szPath);
  514. }
  515. return(hr);
  516. }
  517. // --------------------------------------------------------------------------
  518. // ::SHSetUserPicturePath
  519. //
  520. // Arguments: pszUsername = Desired user (NULL for current user).
  521. // dwFlags = Flags.
  522. // pszPath = Path to NEW user picture.
  523. //
  524. // Returns: HRESULT
  525. //
  526. // Purpose: Sets the specified user's picture as a copy of the given
  527. // image file. The image file may be any supported standard image
  528. // file (.gif / .jpg / .bmp). The file is converted to a 96x96
  529. // .bmp file in the user picture directory.
  530. //
  531. // History: 2000-02-22 vtan created
  532. // 2000-03-24 vtan moved from folder.cpp
  533. // 2000-04-27 jeffreys restore old image on conversion failure
  534. // --------------------------------------------------------------------------
  535. STDAPI SHSetUserPicturePath (LPCTSTR pszUsername, DWORD dwFlags, LPCTSTR pszPath)
  536. {
  537. HRESULT hr;
  538. TCHAR szPath[MAX_PATH];
  539. TCHAR szUsername[UNLEN + sizeof('\0')];
  540. DWORD dwUsernameSize;
  541. hr = E_FAIL;
  542. // Validate dwFlags. Currently no valid flags so this must be 0x00000000.
  543. if ((dwFlags & SHSUPP_FLAG_INVALID_MASK) != 0)
  544. {
  545. return(E_INVALIDARG);
  546. }
  547. dwUsernameSize = ARRAYSIZE(szUsername);
  548. if (GetUserName(szUsername, &dwUsernameSize) == FALSE)
  549. {
  550. return(HRESULT_FROM_WIN32(GetLastError()));
  551. }
  552. if (pszUsername != NULL)
  553. {
  554. // Privilege check. Must be an administrator to use this function when
  555. // pszUsername is not NULL (i.e. for somebody else).
  556. if ((lstrcmpi(pszUsername, szUsername) != 0) &&
  557. (SHTestTokenMembership(NULL, DOMAIN_ALIAS_RID_ADMINS) == FALSE))
  558. {
  559. static const SID c_SystemSid = {SID_REVISION,1,SECURITY_NT_AUTHORITY,{SECURITY_LOCAL_SYSTEM_RID}};
  560. BOOL bSystem = FALSE;
  561. // One more check. Allow local system through since we may
  562. // get called from the logon screen.
  563. if (!CheckTokenMembership(NULL, (PSID)&c_SystemSid, &bSystem) || !bSystem)
  564. {
  565. return(E_ACCESSDENIED);
  566. }
  567. }
  568. }
  569. else
  570. {
  571. pszUsername = szUsername;
  572. }
  573. // Start by getting the base picture path
  574. hr = SHGetFolderPathAndSubDir(NULL,
  575. CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE,
  576. NULL,
  577. SHGFP_TYPE_CURRENT,
  578. UASTR_PATH_PICTURES,
  579. szPath);
  580. if (S_OK == hr)
  581. {
  582. // Before attempt to delete what's there try to access the
  583. // new file. If this fails deleting what's currently installed
  584. // could leave the user without a picture. Fail the API before
  585. // anything is lost.
  586. if ((pszPath == NULL) || (PathFileExistsAndAttributes(pszPath, NULL) != FALSE))
  587. {
  588. TCHAR szTemp[MAX_PATH];
  589. PathAppend(szPath, pszUsername);
  590. hr = StringCchCopy(szTemp, ARRAYSIZE(szTemp), szPath);
  591. if (SUCCEEDED(hr))
  592. {
  593. hr = StringCchCat(szPath, ARRAYSIZE(szPath), TEXT(".bmp"));
  594. if (SUCCEEDED(hr))
  595. {
  596. hr = StringCchCat(szTemp, ARRAYSIZE(szTemp), TEXT(".tmp"));
  597. if (SUCCEEDED(hr))
  598. {
  599. if ((pszPath == NULL) || lstrcmpi(pszPath, szPath) != 0)
  600. {
  601. // If present, rename <username>.Bmp to <username>.Tmp.
  602. // First reset the attributes so file ops work. Don't use
  603. // trace macros because failure is expected.
  604. (BOOL)SetFileAttributes(szPath, 0);
  605. (BOOL)SetFileAttributes(szTemp, 0);
  606. (BOOL)MoveFileEx(szPath, szTemp, MOVEFILE_REPLACE_EXISTING);
  607. // Convert the given image to a bmp and resize it
  608. // using the helper function which does all the goo.
  609. if (pszPath != NULL)
  610. {
  611. hr = ConvertAndResizeImage(pszPath, szPath);
  612. if (SUCCEEDED(hr))
  613. {
  614. // Since this may be an admin setting someone else's
  615. // picture, we need to grant that person access to
  616. // modify/delete the file so they can change it
  617. // themselves later.
  618. (BOOL)SetExplicitAccessToObject(szPath,
  619. SE_FILE_OBJECT,
  620. pszUsername,
  621. GENERIC_READ | GENERIC_EXECUTE | GENERIC_WRITE | DELETE,
  622. 0);
  623. }
  624. }
  625. else
  626. {
  627. hr = S_OK;
  628. }
  629. if (SUCCEEDED(hr))
  630. {
  631. // Delete the old picture
  632. (BOOL)DeleteFile(szTemp);
  633. }
  634. else
  635. {
  636. // Restore the old picture
  637. (BOOL)MoveFileEx(szTemp, szPath, MOVEFILE_REPLACE_EXISTING);
  638. }
  639. // Notify everyone that a user picture has changed
  640. SHChangeDWORDAsIDList dwidl;
  641. dwidl.cb = SIZEOF(dwidl) - SIZEOF(dwidl.cbZero);
  642. dwidl.dwItem1 = SHCNEE_USERINFOCHANGED;
  643. dwidl.dwItem2 = 0;
  644. dwidl.cbZero = 0;
  645. SHChangeNotify(SHCNE_EXTENDED_EVENT, SHCNF_FLUSH, (LPCITEMIDLIST)&dwidl, NULL);
  646. }
  647. else
  648. {
  649. // Source and destination are the same, nothing to do.
  650. hr = S_FALSE;
  651. }
  652. }
  653. }
  654. }
  655. }
  656. else
  657. {
  658. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  659. }
  660. }
  661. return(hr);
  662. }
  663. #pragma warning(pop)