Commit 34548a8e authored by Tien Mai's avatar Tien Mai Committed by Commit Bot

[GCPW] Create bitmap version of profile picture used for reauth

- Linked agains gdi+ to use their image converting tools.
- Now when downloading the profile picture, the largest downloaded
picture will also be converted into .bmp and saved into the same
directory.
- Override the GetBitmapValue of the credential to read this bmp
file and return it.

Bug: 936847
Change-Id: Ie36b99e167c828ed7b46b88c400465f5fca44fd2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1497192
Commit-Queue: Tien Mai <tienmai@chromium.org>
Reviewed-by: default avatarRoger Tawa <rogerta@chromium.org>
Cr-Commit-Position: refs/heads/master@{#638212}
parent f843ae38
...@@ -141,6 +141,7 @@ config("gaiacp_config") { ...@@ -141,6 +141,7 @@ config("gaiacp_config") {
"advapi32.lib", # For LookupAccountSid() "advapi32.lib", # For LookupAccountSid()
"credui.lib", # For CredPackAuthenticationBufferW() "credui.lib", # For CredPackAuthenticationBufferW()
"crypt32.lib", # For CryptProtectData() "crypt32.lib", # For CryptProtectData()
"gdiplus.lib", # To convert profile pictures to bmp.
"netapi32.lib", # For NetUserXXX() "netapi32.lib", # For NetUserXXX()
"userenv.lib", # For GetUserProfileDirectoryW() "userenv.lib", # For GetUserProfileDirectoryW()
"winhttp.lib", # For WinHttpXXX() "winhttp.lib", # For WinHttpXXX()
......
...@@ -590,6 +590,23 @@ HRESULT CGaiaCredentialBase::GetStringValueImpl(DWORD field_id, ...@@ -590,6 +590,23 @@ HRESULT CGaiaCredentialBase::GetStringValueImpl(DWORD field_id,
return hr; return hr;
} }
HRESULT CGaiaCredentialBase::GetBitmapValueImpl(DWORD field_id,
HBITMAP* phbmp) {
HRESULT hr = E_INVALIDARG;
switch (field_id) {
case FID_PROVIDER_LOGO:
*phbmp = ::LoadBitmap(CURRENT_MODULE(),
MAKEINTRESOURCE(IDB_GOOGLE_LOGO_SMALL));
if (*phbmp)
hr = S_OK;
break;
default:
break;
}
return hr;
}
void CGaiaCredentialBase::ResetInternalState() { void CGaiaCredentialBase::ResetInternalState() {
LOGFN(INFO); LOGFN(INFO);
username_.Empty(); username_.Empty();
...@@ -910,19 +927,7 @@ HRESULT CGaiaCredentialBase::GetStringValue(DWORD field_id, wchar_t** value) { ...@@ -910,19 +927,7 @@ HRESULT CGaiaCredentialBase::GetStringValue(DWORD field_id, wchar_t** value) {
} }
HRESULT CGaiaCredentialBase::GetBitmapValue(DWORD field_id, HBITMAP* phbmp) { HRESULT CGaiaCredentialBase::GetBitmapValue(DWORD field_id, HBITMAP* phbmp) {
HRESULT hr = E_INVALIDARG; return GetBitmapValueImpl(field_id, phbmp);
switch (field_id) {
case FID_PROVIDER_LOGO:
*phbmp = ::LoadBitmap(CURRENT_MODULE(),
MAKEINTRESOURCE(IDB_GOOGLE_LOGO_SMALL));
if (*phbmp)
hr = S_OK;
break;
default:
break;
}
return hr;
} }
HRESULT CGaiaCredentialBase::GetCheckboxValue(DWORD field_id, HRESULT CGaiaCredentialBase::GetCheckboxValue(DWORD field_id,
......
...@@ -125,6 +125,10 @@ class ATL_NO_VTABLE CGaiaCredentialBase ...@@ -125,6 +125,10 @@ class ATL_NO_VTABLE CGaiaCredentialBase
// Gets the string value for the given credential UI field. // Gets the string value for the given credential UI field.
virtual HRESULT GetStringValueImpl(DWORD field_id, wchar_t** value); virtual HRESULT GetStringValueImpl(DWORD field_id, wchar_t** value);
// Can be overridden to change the icon used for anonymous tiles when they are
// shown in the selection list on the side and when they are selected.
virtual HRESULT GetBitmapValueImpl(DWORD field_id, HBITMAP* phbmp);
// Resets the state of the credential, forgetting any username or password // Resets the state of the credential, forgetting any username or password
// that may have been set previously. Derived classes may override to // that may have been set previously. Derived classes may override to
// perform more state resetting if needed, but should always call the base // perform more state resetting if needed, but should always call the base
......
...@@ -9,9 +9,10 @@ ...@@ -9,9 +9,10 @@
#include <winternl.h> #include <winternl.h>
#define _NTDEF_ // Prevent redefition errors, must come after <winternl.h> #define _NTDEF_ // Prevent redefition errors, must come after <winternl.h>
#include <ntsecapi.h> // For LsaLookupAuthenticationPackage() #include <ntsecapi.h> // For LsaLookupAuthenticationPackage()
#include <sddl.h> // For ConvertSidToStringSid() #include <sddl.h> // For ConvertSidToStringSid()
#include <security.h> // For NEGOSSP_NAME_A #include <security.h> // For NEGOSSP_NAME_A
#include <shlobj.h> // For SHGetKnownFolderPath()
#include <wbemidl.h> #include <wbemidl.h>
#include <atlbase.h> #include <atlbase.h>
...@@ -45,6 +46,9 @@ ...@@ -45,6 +46,9 @@
namespace credential_provider { namespace credential_provider {
const wchar_t kDefaultProfilePictureFileExtension[] = L".jpg";
const wchar_t kCredentialLogoPictureFileExtension[] = L".bmp";
namespace { namespace {
constexpr char kSentinelFilename[] = "gcpw_startup.sentinel"; constexpr char kSentinelFilename[] = "gcpw_startup.sentinel";
...@@ -634,6 +638,29 @@ base::string16 GetStringResource(int base_message_id) { ...@@ -634,6 +638,29 @@ base::string16 GetStringResource(int base_message_id) {
return localized_string; return localized_string;
} }
HRESULT GetUserAccountPicturePath(const base::string16& sid,
base::FilePath* base_path) {
DCHECK(base_path);
base_path->clear();
LPWSTR path;
HRESULT hr = ::SHGetKnownFolderPath(FOLDERID_PublicUserTiles, 0, NULL, &path);
if (FAILED(hr)) {
LOGFN(ERROR) << "SHGetKnownFolderPath=" << putHR(hr);
return hr;
}
*base_path = base::FilePath(path).Append(sid);
::CoTaskMemFree(path);
return S_OK;
}
base::FilePath GetUserSizedAccountPictureFilePath(
const base::FilePath& account_picture_path,
int size,
const base::string16& picture_extension) {
return account_picture_path.Append(base::StringPrintf(
L"GoogleAccountPicture_%i%ls", size, picture_extension.c_str()));
}
base::string16 GetSelectedLanguage() { base::string16 GetSelectedLanguage() {
return GetLanguageSelector().matched_candidate(); return GetLanguageSelector().matched_candidate();
} }
......
...@@ -54,6 +54,17 @@ constexpr int kMaxUsernameAttempts = 10; ...@@ -54,6 +54,17 @@ constexpr int kMaxUsernameAttempts = 10;
// already exists. // already exists.
constexpr int kInitialDuplicateUsernameIndex = 2; constexpr int kInitialDuplicateUsernameIndex = 2;
// Default extension used as a fallback if the picture_url returned from gaia
// does not have a file extension.
extern const wchar_t kDefaultProfilePictureFileExtension[];
// Required extension for the picture that will be shown by the credential on
// the login screen. Windows only supports .bmp files for the images shown by
// credentials.
extern const wchar_t kCredentialLogoPictureFileExtension[];
constexpr int kLargestProfilePictureSize = 448;
// Because of some strange dependency problems with windows header files, // Because of some strange dependency problems with windows header files,
// define STATUS_SUCCESS here instead of including ntstatus.h or SubAuth.h // define STATUS_SUCCESS here instead of including ntstatus.h or SubAuth.h
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) #define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
...@@ -200,6 +211,20 @@ void DeleteStartupSentinel(); ...@@ -200,6 +211,20 @@ void DeleteStartupSentinel();
// Gets a string resource from the DLL with the given id. // Gets a string resource from the DLL with the given id.
base::string16 GetStringResource(int base_message_id); base::string16 GetStringResource(int base_message_id);
// Fills |base_path| with the path where user profile pictures are stored for
// user with |sid|. This function can fail if the known folder
// FOLDERID_PublicUserTiles cannot be found.
HRESULT GetUserAccountPicturePath(const base::string16& sid,
base::FilePath* base_path);
// Returns the full path to a user profile picture of a specific |size| and
// |picture_extension|. |account_picture_path| is path filled in by a call to
// GetUserAccountPicturePath.
base::FilePath GetUserSizedAccountPictureFilePath(
const base::FilePath& account_picture_path,
int size,
const base::string16& picture_extension);
// Gets the language selected by the base::win::i18n::LanguageSelector. // Gets the language selected by the base::win::i18n::LanguageSelector.
base::string16 GetSelectedLanguage(); base::string16 GetSelectedLanguage();
......
...@@ -4,8 +4,12 @@ ...@@ -4,8 +4,12 @@
#include "chrome/credential_provider/gaiacp/reauth_credential_anonymous.h" #include "chrome/credential_provider/gaiacp/reauth_credential_anonymous.h"
#include "base/files/file_util.h"
#include "chrome/credential_provider/common/gcp_strings.h"
#include "chrome/credential_provider/gaiacp/gcp_utils.h"
#include "chrome/credential_provider/gaiacp/logging.h" #include "chrome/credential_provider/gaiacp/logging.h"
#include "chrome/credential_provider/gaiacp/os_user_manager.h" #include "chrome/credential_provider/gaiacp/os_user_manager.h"
#include "chrome/credential_provider/gaiacp/reg_utils.h"
namespace credential_provider { namespace credential_provider {
...@@ -37,4 +41,37 @@ HRESULT CReauthCredentialAnonymous::GetStringValueImpl(DWORD field_id, ...@@ -37,4 +41,37 @@ HRESULT CReauthCredentialAnonymous::GetStringValueImpl(DWORD field_id,
return ::SHStrDupW(fullname.c_str(), value); return ::SHStrDupW(fullname.c_str(), value);
} }
HRESULT CReauthCredentialAnonymous::GetBitmapValueImpl(DWORD field_id,
HBITMAP* phbmp) {
if (field_id == FID_PROVIDER_LOGO) {
base::FilePath account_picture_path;
HRESULT hr = GetUserAccountPicturePath(OLE2W(get_os_user_sid()),
&account_picture_path);
if (SUCCEEDED(hr)) {
base::FilePath target_picture_path = GetUserSizedAccountPictureFilePath(
account_picture_path, kLargestProfilePictureSize,
kCredentialLogoPictureFileExtension);
if (base::PathExists(target_picture_path)) {
*phbmp = static_cast<HBITMAP>(
::LoadImage(nullptr, target_picture_path.value().c_str(),
IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE));
if (*phbmp) {
return S_OK;
} else {
hr = HRESULT_FROM_WIN32(::GetLastError());
LOGFN(ERROR) << "Failed load image at='"
<< target_picture_path.value() << "' hr=" << putHR(hr);
}
} else {
LOGFN(ERROR) << "Required picture at='" << target_picture_path.value()
<< "' does not exist.";
}
} else {
LOGFN(ERROR) << "Failed to get account picture known folder hr="
<< putHR(hr);
}
}
return CReauthCredentialBase::GetBitmapValueImpl(field_id, phbmp);
}
} // namespace credential_provider } // namespace credential_provider
...@@ -39,6 +39,7 @@ class ATL_NO_VTABLE CReauthCredentialAnonymous ...@@ -39,6 +39,7 @@ class ATL_NO_VTABLE CReauthCredentialAnonymous
DECLARE_PROTECT_FINAL_CONSTRUCT() DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT GetStringValueImpl(DWORD field_id, wchar_t** value) override; HRESULT GetStringValueImpl(DWORD field_id, wchar_t** value) override;
HRESULT GetBitmapValueImpl(DWORD field_id, HBITMAP* phbmp) override;
}; };
} // namespace credential_provider } // namespace credential_provider
......
...@@ -4,16 +4,34 @@ ...@@ -4,16 +4,34 @@
#include "chrome/credential_provider/gaiacp/scoped_user_profile.h" #include "chrome/credential_provider/gaiacp/scoped_user_profile.h"
// Must appear before gdiplus.h
// gdiplus.h requires global functions min and max to exist. But the usage of
// these functions occurs in the Gdiplus namespace only so we just declare
// std::min and std::max in this namespace so that gdiplus can find them.
// This is similar to what was done in:
// https://cs.chromium.org/chromium/src/third_party/pdfium/core/fxge/win32/fx_win32_gdipext.cpp?type=cs&q=gdiplus.h&g=0&l=29
namespace Gdiplus {
using std::max;
using std::min;
} // namespace Gdiplus
#include <Windows.h> #include <Windows.h>
#include <atlcomcli.h>
#include <atlconv.h> #include <atlconv.h>
#include <dpapi.h> #include <dpapi.h>
#include <gdiplus.h>
#include <objidl.h>
#include <security.h> #include <security.h>
#include <shlobj.h> #include <shlobj.h>
#include <shlwapi.h>
#include <userenv.h> #include <userenv.h>
#include "base/bind.h"
#include "base/callback.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h" #include "base/win/registry.h"
...@@ -35,9 +53,8 @@ namespace { ...@@ -35,9 +53,8 @@ namespace {
// retrying would not be needed, but this notification does not exist. // retrying would not be needed, but this notification does not exist.
const int kWaitForProfileCreationRetryCount = 30; const int kWaitForProfileCreationRetryCount = 30;
constexpr wchar_t kDefaultProfilePictureFileExtension[] = L".jpg"; constexpr int kProfilePictureSizes[] = {
32, 40, 48, 96, 192, 240, kLargestProfilePictureSize};
constexpr int kProfilePictureSizes[] = {32, 40, 48, 96, 192, 240, 448};
std::string GetEncryptedRefreshToken( std::string GetEncryptedRefreshToken(
base::win::ScopedHandle::Handle logon_handle, base::win::ScopedHandle::Handle logon_handle,
...@@ -78,20 +95,139 @@ std::string GetEncryptedRefreshToken( ...@@ -78,20 +95,139 @@ std::string GetEncryptedRefreshToken(
return encrypted_data; return encrypted_data;
} }
HRESULT GetBaseAccountPicturePath(base::FilePath* base_path) { HRESULT GetEncoderClsidByExtension(const base::string16& desired_extension,
DCHECK(base_path); CLSID* clsid_out) {
base_path->clear(); DCHECK(clsid_out);
LPWSTR path; // Number of image encoders.
HRESULT hr = ::SHGetKnownFolderPath(FOLDERID_PublicUserTiles, 0, NULL, &path); UINT num = 0;
// Size of the image encoder array in bytes.
UINT size = 0;
Gdiplus::ImageCodecInfo* image_codec_info = nullptr;
Gdiplus::GetImageEncodersSize(&num, &size);
if (size == 0)
return E_FAIL;
std::unique_ptr<char[]> encoder_buffer(new char[size]);
image_codec_info =
reinterpret_cast<Gdiplus::ImageCodecInfo*>(encoder_buffer.get());
if (image_codec_info == nullptr)
return E_FAIL;
Gdiplus::GetImageEncoders(num, size, image_codec_info);
for (UINT j = 0; j < num; ++j) {
// FilenameExtension is a semicolon separated list of extensions recognized
// by the codec. Each extension is in the format "*.{ext}" so the * needs to
// be removed to get the real extension.
std::vector<base::string16> codec_extensions = base::SplitString(
base::StringPiece16(image_codec_info[j].FilenameExtension), L";",
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
for (auto& extension : codec_extensions) {
size_t first_period = extension.find_last_of('.');
if (first_period != base::string16::npos &&
base::EqualsCaseInsensitiveASCII(extension.substr(first_period),
desired_extension)) {
*clsid_out = image_codec_info[j].Clsid;
return S_OK;
}
}
}
return E_FAIL;
}
HRESULT ConvertImageToDesiredFormat(const std::vector<char>& image_buffer,
const base::FilePath& converted_path) {
// Only support conversion to |kCredentialLogoPictureFileExtension| for now.
DCHECK(base::EqualsCaseInsensitiveASCII(converted_path.Extension(),
kCredentialLogoPictureFileExtension));
// Initialize GDI+.
Gdiplus::GdiplusStartupInput gdiplus_startup_input;
ULONG_PTR gdiplus_token;
Gdiplus::GdiplusStartup(&gdiplus_token, &gdiplus_startup_input, NULL);
// Load the image stream into memory. Gdiplus::Image can automatically detect
// the file type and load the correct contents. Note that gaia returns a
// picture url with a .jpg extension but when the url is downloaded the image
// is actually a .png format and the Image class can handle this case
// correctly here.
CComPtr<IStream> buffer_stream;
buffer_stream.Attach(::SHCreateMemStream(
reinterpret_cast<const BYTE*>(image_buffer.data()), image_buffer.size()));
std::unique_ptr<Gdiplus::Image> image =
std::make_unique<Gdiplus::Image>(buffer_stream);
if (image->GetType() == Gdiplus::ImageTypeUnknown) {
LOGFN(ERROR) << "Unknown image type when loading image stream";
Gdiplus::GdiplusShutdown(gdiplus_token);
return E_FAIL;
}
// Get the CLSID of the encoder to the desired file type.
CLSID encoder_clsid;
HRESULT hr =
GetEncoderClsidByExtension(converted_path.Extension(), &encoder_clsid);
if (FAILED(hr)) { if (FAILED(hr)) {
LOGFN(ERROR) << "SHGetKnownFolderPath=" << putHR(hr); LOGFN(ERROR) << "GetEncoderClsid hr=" << putHR(hr);
Gdiplus::GdiplusShutdown(gdiplus_token);
return hr; return hr;
} }
*base_path = base::FilePath(path);
::CoTaskMemFree(path); Gdiplus::Status stat =
image->Save(converted_path.value().c_str(), &encoder_clsid, nullptr);
Gdiplus::GdiplusShutdown(gdiplus_token);
if (stat != Gdiplus::Ok) {
LOGFN(ERROR) << "image->Save stat=" << stat;
return E_FAIL;
}
return S_OK; return S_OK;
} }
using ImageProcessor =
base::OnceCallback<HRESULT(const base::FilePath& picture_path,
const std::vector<char>& picture_buffer)>;
HRESULT SaveProcessedProfilePictureToDisk(
const base::FilePath& picture_path,
const std::vector<char>& picture_buffer,
ImageProcessor processor_function) {
DCHECK(processor_function);
// Make the file visible in case it is hidden or else WriteFile will fail
// to overwrite the existing file.
DWORD file_attributes = ::GetFileAttributes(picture_path.value().c_str());
if (file_attributes != INVALID_FILE_ATTRIBUTES) {
if (!::SetFileAttributes(picture_path.value().c_str(),
file_attributes & ~FILE_ATTRIBUTE_HIDDEN)) {
LOGFN(ERROR) << "SetFileAttributes(remove hidden) err="
<< ::GetLastError();
}
}
HRESULT hr = std::move(processor_function).Run(picture_path, picture_buffer);
if (SUCCEEDED(hr)) {
// Make the picture file hidden just like the system would normally.
file_attributes = ::GetFileAttributes(picture_path.value().c_str());
if (file_attributes != INVALID_FILE_ATTRIBUTES) {
if (!::SetFileAttributes(picture_path.value().c_str(),
file_attributes | FILE_ATTRIBUTE_HIDDEN)) {
LOGFN(ERROR) << "SetFileAttributes(add hidden) err="
<< ::GetLastError();
}
}
}
return hr;
}
HRESULT UpdateProfilePicturesForWindows8AndNewer( HRESULT UpdateProfilePicturesForWindows8AndNewer(
const base::string16& sid, const base::string16& sid,
const base::string16& picture_url, const base::string16& picture_url,
...@@ -102,7 +238,7 @@ HRESULT UpdateProfilePicturesForWindows8AndNewer( ...@@ -102,7 +238,7 @@ HRESULT UpdateProfilePicturesForWindows8AndNewer(
// Try to download profile pictures of all required sizes for windows. // Try to download profile pictures of all required sizes for windows.
// Needed profile picture sizes are in |kProfilePictureSizes|. // Needed profile picture sizes are in |kProfilePictureSizes|.
// The way Windows8+ stores sets profile pictures is the following: // The way Windows8+ stores profile pictures is the following:
// In |reg_utils.cc:kAccountPicturesRootRegKey| there is a registry key // In |reg_utils.cc:kAccountPicturesRootRegKey| there is a registry key
// for each resolution of profile picture needed. The keys are names // for each resolution of profile picture needed. The keys are names
// "Image[x]" where [x] is the resolution of the picture. // "Image[x]" where [x] is the resolution of the picture.
...@@ -116,16 +252,13 @@ HRESULT UpdateProfilePicturesForWindows8AndNewer( ...@@ -116,16 +252,13 @@ HRESULT UpdateProfilePicturesForWindows8AndNewer(
return E_FAIL; return E_FAIL;
} }
base::FilePath account_picture_base_path; base::FilePath account_picture_path;
HRESULT hr = GetBaseAccountPicturePath(&account_picture_base_path); HRESULT hr = GetUserAccountPicturePath(sid, &account_picture_path);
if (FAILED(hr)) { if (FAILED(hr)) {
LOGFN(ERROR) << "Failed to get account picture known folder=" << putHR(hr); LOGFN(ERROR) << "Failed to get account picture known folder=" << putHR(hr);
return E_FAIL; return E_FAIL;
} }
base::FilePath account_picture_path =
account_picture_base_path.Append(sid.c_str());
if (!base::PathExists(account_picture_path) && if (!base::PathExists(account_picture_path) &&
!base::CreateDirectory(account_picture_path)) { !base::CreateDirectory(account_picture_path)) {
LOGFN(ERROR) << "Failed to create profile picture directory=" LOGFN(ERROR) << "Failed to create profile picture directory="
...@@ -133,24 +266,25 @@ HRESULT UpdateProfilePicturesForWindows8AndNewer( ...@@ -133,24 +266,25 @@ HRESULT UpdateProfilePicturesForWindows8AndNewer(
return E_FAIL; return E_FAIL;
} }
constexpr wchar_t kBasePictureFilename[] = L"GoogleAccountPicture";
base::string16 base_picture_extension = kDefaultProfilePictureFileExtension; base::string16 base_picture_extension = kDefaultProfilePictureFileExtension;
base::string16 base_picture_filename = kBasePictureFilename;
base_picture_filename += L"_";
size_t last_period = picture_url_path.find_last_of('.'); size_t last_period = picture_url_path.find_last_of('.');
if (last_period != std::string::npos) { if (last_period != std::string::npos)
base_picture_extension = picture_url_path.substr(last_period); base_picture_extension = picture_url_path.substr(last_period);
}
for (auto image_size : kProfilePictureSizes) { for (auto image_size : kProfilePictureSizes) {
base::string16 image_size_postfix = base::StringPrintf(L"%i", image_size); base::FilePath target_picture_path = GetUserSizedAccountPictureFilePath(
base::FilePath target_picture_path = account_picture_path.Append( account_picture_path, image_size, base_picture_extension);
base_picture_filename + image_size_postfix + base_picture_extension); base::FilePath bmp_picture_path = target_picture_path.ReplaceExtension(
kCredentialLogoPictureFileExtension);
bool needs_to_save_original =
force_update || !base::PathExists(target_picture_path);
bool needs_to_save_bitmap =
force_update || (image_size == kLargestProfilePictureSize &&
!base::PathExists(bmp_picture_path));
// Skip if the file already exists and an update is not forced. // Skip if the file already exists and an update is not forced.
if (!force_update && base::PathExists(target_picture_path)) { if (!needs_to_save_original && !needs_to_save_bitmap) {
// Update the reg string for the image if it is not up to date. // Update the reg string for the image if it is not up to date.
wchar_t old_picture_path[MAX_PATH]; wchar_t old_picture_path[MAX_PATH];
ULONG path_size = base::size(old_picture_path); ULONG path_size = base::size(old_picture_path);
...@@ -181,33 +315,48 @@ HRESULT UpdateProfilePicturesForWindows8AndNewer( ...@@ -181,33 +315,48 @@ HRESULT UpdateProfilePicturesForWindows8AndNewer(
continue; continue;
} }
// Make the file visible in case it is hidden or else WriteFile will fail if (needs_to_save_original) {
// to overwrite the existing file. SaveProcessedProfilePictureToDisk(
DWORD file_attributes = target_picture_path, response,
::GetFileAttributes(target_picture_path.value().c_str()); base::BindOnce(
if (file_attributes != INVALID_FILE_ATTRIBUTES) { [](const base::string16& sid, int image_size,
::SetFileAttributes(target_picture_path.value().c_str(), const base::FilePath& picture_path,
file_attributes & ~FILE_ATTRIBUTE_HIDDEN); const std::vector<char>& picture_buffer) {
} HRESULT hr = S_OK;
if (base::WriteFile(target_picture_path, response.data(), if (base::WriteFile(picture_path, picture_buffer.data(),
response.size()) != static_cast<int>(response.size())) { picture_buffer.size()) !=
LOGFN(ERROR) << "Failed to write profile picture to file=" static_cast<int>(picture_buffer.size())) {
<< target_picture_path; LOGFN(ERROR) << "Failed to write profile picture to file="
continue; << picture_path;
hr = HRESULT_FROM_WIN32(::GetLastError());
} else {
// Finally update the registry to point to this profile
// picture.
HRESULT reg_hr = SetAccountPictureRegString(
sid, image_size, picture_path.value());
if (FAILED(reg_hr))
LOGFN(ERROR) << "SetAccountPictureRegString(pic) hr="
<< putHR(reg_hr);
}
return hr;
},
sid, image_size));
} }
// Make the picture file hidden just like the system would normally. if (needs_to_save_bitmap) {
file_attributes = ::GetFileAttributes(target_picture_path.value().c_str()); SaveProcessedProfilePictureToDisk(
if (file_attributes != INVALID_FILE_ATTRIBUTES) { bmp_picture_path, response,
::SetFileAttributes(target_picture_path.value().c_str(), base::BindOnce([](const base::FilePath& picture_path,
file_attributes | FILE_ATTRIBUTE_HIDDEN); const std::vector<char>& picture_buffer) {
HRESULT hr =
ConvertImageToDesiredFormat(picture_buffer, picture_path);
if (FAILED(hr))
LOGFN(ERROR) << "ConvertImageToDesiredFormat(pic) hr="
<< putHR(hr);
return hr;
}));
} }
// Finally update the registry to point to this profile picture.
hr = SetAccountPictureRegString(sid, image_size,
target_picture_path.value());
if (FAILED(hr))
LOGFN(ERROR) << "SetAccountPictureRegString(pic) hr=" << putHR(hr);
} }
return S_OK; return S_OK;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment