Make sure mutex is always initialized
[TortoiseGit.git] / src / TortoiseProc / GravatarPictureBox.cpp
blob6859aedbbf90c4cace810c97d32c6f0da7767499
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2013 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include "stdafx.h"
20 #include "GravatarPictureBox.h"
21 #include "Picture.h"
22 #include "MessageBox.h"
23 #include "UnicodeUtils.h"
24 #include "Git.h"
25 #include "LoglistCommonResource.h"
26 #include "resource.h"
28 static CString CalcMD5(CString text)
30 HCRYPTPROV hProv = 0;
31 if (!CryptAcquireContext(&hProv, nullptr, nullptr, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
32 return _T("");
34 HCRYPTHASH hHash = 0;
35 if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash))
37 CryptReleaseContext(hProv, 0);
38 return _T("");
41 CStringA textA = CUnicodeUtils::GetUTF8(text);
42 if (!CryptHashData(hHash, (LPBYTE)textA.GetBuffer(), textA.GetLength(), 0))
44 CryptReleaseContext(hProv, 0);
45 CryptDestroyHash(hHash);
46 return _T("");
49 CString hash;
50 BYTE rgbHash[16];
51 DWORD cbHash = _countof(rgbHash);
52 if (!CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0))
53 return _T("");
54 for (DWORD i = 0; i < cbHash; i++)
56 BYTE hi = rgbHash[i] >> 4;
57 BYTE lo = rgbHash[i] & 0xf;
58 hash.AppendChar(hi + (hi > 9 ? 87 : 48));
59 hash.AppendChar(lo + (lo > 9 ? 87 : 48));
62 CryptDestroyHash(hHash);
63 CryptReleaseContext(hProv, 0);
64 return hash;
67 BEGIN_MESSAGE_MAP(CGravatar, CStatic)
68 ON_WM_PAINT()
69 END_MESSAGE_MAP()
71 CGravatar::CGravatar()
72 : CStatic()
73 , m_gravatarEvent(INVALID_HANDLE_VALUE)
74 , m_gravatarThread(nullptr)
75 , m_gravatarExit(false)
76 , m_bEnableGravatar(false)
78 m_gravatarLock.Init();
81 CGravatar::~CGravatar()
83 SafeTerminateGravatarThread();
84 m_gravatarLock.Term();
85 if (m_gravatarEvent)
86 CloseHandle(m_gravatarEvent);
89 void CGravatar::Init()
91 if (m_bEnableGravatar)
93 if (m_gravatarEvent == INVALID_HANDLE_VALUE)
94 m_gravatarEvent = ::CreateEvent(nullptr, FALSE, TRUE, nullptr);
95 if (m_gravatarThread == nullptr)
97 m_gravatarThread = AfxBeginThread([] (LPVOID lpVoid) -> UINT { ((CGravatar *)lpVoid)->GravatarThread(); return 0; }, this, THREAD_PRIORITY_BELOW_NORMAL);
98 if (m_gravatarThread == nullptr)
100 CMessageBox::Show(nullptr, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
101 return;
107 void CGravatar::LoadGravatar(CString email)
109 if (m_gravatarThread == nullptr)
110 return;
112 if (email.IsEmpty())
114 m_gravatarLock.Lock();
115 if (!m_filename.IsEmpty())
117 m_filename = _T("");
118 m_gravatarLock.Unlock();
119 Invalidate();
121 else
122 m_gravatarLock.Unlock();
123 return;
126 m_gravatarLock.Lock();
127 bool diff = m_email != email;
128 m_email = email;
129 m_gravatarLock.Unlock();
130 if (diff)
131 ::SetEvent(m_gravatarEvent);
134 void CGravatar::GravatarThread()
136 CString gravatarBaseUrl = CRegString(_T("Software\\TortoiseGit\\GravatarUrl"), _T("http://www.gravatar.com/avatar/%HASH%"));
137 CoInitialize(nullptr);
138 while (!m_gravatarExit)
140 ::WaitForSingleObject(m_gravatarEvent, INFINITE);
141 while (!m_gravatarExit)
143 m_gravatarLock.Lock();
144 CString email = m_email;
145 m_gravatarLock.Unlock();
146 if (email.IsEmpty())
147 break;
149 Sleep(500);
150 m_gravatarLock.Lock();
151 bool diff = email != m_email;
152 m_gravatarLock.Unlock();
153 if (diff)
154 continue;
156 CString md5 = CalcMD5(email);
157 if (md5.IsEmpty())
158 continue;
160 CString gravatarUrl = gravatarBaseUrl;
161 gravatarUrl.Replace(_T("%HASH%"), md5);
162 CString tempFile;
163 GetTempPath(tempFile);
164 tempFile += md5;
165 if (PathFileExists(tempFile))
167 m_filename = tempFile;
168 m_email = _T("");
170 else
172 HRESULT res = URLDownloadToFile(nullptr, gravatarUrl, tempFile, 0, nullptr);
173 m_gravatarLock.Lock();
174 if (m_email == email && res == S_OK)
176 HANDLE hFile = CreateFile(tempFile, FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
177 FILETIME creationTime = {};
178 GetFileTime(hFile, &creationTime, nullptr, nullptr);
179 uint64_t delta = 7 * 24 * 60 * 60 * 10000000LL;
180 DWORD low = creationTime.dwLowDateTime;
181 creationTime.dwLowDateTime += delta & 0xffffffff;
182 creationTime.dwHighDateTime += delta >> 32;
183 if (creationTime.dwLowDateTime < low)
184 creationTime.dwHighDateTime++;
185 SetFileTime(hFile, &creationTime, nullptr, nullptr);
186 CloseHandle(hFile);
187 m_filename = tempFile;
188 m_email = _T("");
190 else
191 m_filename = _T("");
193 m_gravatarLock.Unlock();
194 Invalidate();
197 CoUninitialize();
200 void CGravatar::SafeTerminateGravatarThread()
202 if (m_gravatarThread)
204 ::SetEvent(m_gravatarEvent);
205 m_gravatarThread = nullptr;
209 void CGravatar::OnPaint()
211 CPaintDC dc(this);
212 RECT rect;
213 GetClientRect(&rect);
214 dc.FillSolidRect(&rect, GetSysColor(COLOR_BTNFACE));
215 m_gravatarLock.Lock();
216 CString filename = m_filename;
217 m_gravatarLock.Unlock();
218 if (filename.IsEmpty()) return;
219 CPicture picture;
220 picture.Load(filename.GetString());
221 picture.Show(dc, rect);