Audacity 3.2.0
UserService.cpp
Go to the documentation of this file.
1/* SPDX-License-Identifier: GPL-2.0-or-later */
2/*!********************************************************************
3
4 Audacity: A Digital Audio Editor
5
6 UserService.cpp
7
8 Dmitry Vedenko
9
10**********************************************************************/
11
12#include "UserService.h"
13
14#include <memory>
15#include <vector>
16
17#include <wx/file.h>
18
19#include <rapidjson/document.h>
20
21#include "ServiceConfig.h"
22#include "OAuthService.h"
23
24#include "BasicUI.h"
25#include "FileNames.h"
26#include "Observer.h"
27#include "Prefs.h"
28
29#include "IResponse.h"
30#include "NetworkManager.h"
31#include "Request.h"
32
33#include "CodeConversions.h"
34
36{
37namespace
38{
40{
41 const wxFileName avatarFileName(FileNames::ConfigDir(), "avatar");
42 return avatarFileName.GetFullPath();
43}
44
45StringSetting userName { L"/cloud/audiocom/userName", "" };
46StringSetting displayName { L"/cloud/audiocom/displayName", "" };
47StringSetting avatarEtag { L"/cloud/audiocom/avatarEtag", "" };
48
51 [](const auto& state)
52 {
53 if (state.authorised)
54 GetUserService().UpdateUserData();
55 else
57 });
58
59} // namespace
60
62{
63 auto& oauthService = GetOAuthService();
64
65 if (!oauthService.HasAccessToken())
66 return;
67
68 using namespace audacity::network_manager;
69
70 Request request(GetServiceConfig().GetAPIUrl("/me"));
71
72 request.setHeader(
74 std::string(oauthService.GetAccessToken()));
75
76 request.setHeader(
78
79 auto response = NetworkManager::GetInstance().doGet(request);
80
81 response->setRequestFinishedCallback(
82 [response, this](auto)
83 {
84 const auto httpCode = response->getHTTPCode();
85
86 if (httpCode != 200)
87 return;
88
89 const auto body = response->readAll<std::string>();
90
91 using namespace rapidjson;
92
93 Document document;
94 document.Parse(body.data(), body.size());
95
96 if (!document.IsObject())
97 return;
98
99 const auto username = document["username"].GetString();
100 const auto avatar = document["avatar"].GetString();
101 const auto profileName = document["profile"]["name"].GetString();
102
104 [this, username = std::string(username),
105 profileName = std::string(profileName),
106 avatar = std::string(avatar)]()
107 {
110
111 gPrefs->Flush();
112
113 DownloadAvatar(avatar);
114
115 Publish({});
116 });
117 });
118}
119
121{
123 [this]()
124 {
125 // No valid data was present, do not spam Publish()
126 if (GetUserSlug().empty())
127 return;
128
129 userName.Write({});
130 displayName.Write({});
131 avatarEtag.Write({});
132
133 gPrefs->Flush();
134
135 Publish({});
136 });
137}
138
140{
141 static UserService userService;
142 return userService;
143}
144
145void UserService::DownloadAvatar(std::string_view url)
146{
147 const auto avatarPath = MakeAvatarPath();
148 const auto avatarTempPath = avatarPath + ".tmp";
149
150 if (url.empty())
151 {
152 if (wxFileExists(avatarPath))
153 wxRemoveFile(avatarPath);
154
155 return;
156 }
157
158 std::shared_ptr<wxFile> avatarFile = std::make_shared<wxFile>();
159
160 if (!avatarFile->Create(avatarTempPath, true))
161 return;
162
163 using namespace audacity::network_manager;
164
165 auto request = Request(std::string(url));
166
167 const auto etag = audacity::ToUTF8(avatarEtag.Read());
168
169 // If ETag is present - use it to prevent re-downloading the same file
170 if (!etag.empty() && wxFileExists(avatarPath))
171 request.setHeader(common_headers::IfNoneMatch, etag);
172
173 auto response = NetworkManager::GetInstance().doGet(request);
174
175 response->setOnDataReceivedCallback(
176 [response, avatarFile](auto)
177 {
178 std::vector<char> buffer(response->getBytesAvailable());
179
180 size_t bytes = response->readData(buffer.data(), buffer.size());
181
182 avatarFile->Write(buffer.data(), buffer.size());
183 });
184
185 response->setRequestFinishedCallback(
186 [response, avatarFile, avatarPath, avatarTempPath, this](auto)
187 {
188 avatarFile->Close();
189
190 const auto httpCode = response->getHTTPCode();
191
192 if (httpCode != 200)
193 {
194 // For any response except 200 just remove the temp file
195 wxRemoveFile(avatarTempPath);
196 return;
197 }
198
199 const auto etag = response->getHeader("ETag");
200 const auto oldPath = avatarPath + ".old";
201
202 if (wxFileExists(avatarPath))
203 if (!wxRenameFile(avatarPath, oldPath))
204 return;
205
206 if (!wxRenameFile(avatarTempPath, avatarPath))
207 {
208 // Try at least to get it back...
209 wxRenameFile(oldPath, avatarPath);
210 return;
211 }
212
213 if (wxFileExists(oldPath))
214 wxRemoveFile(oldPath);
215
217 [this, etag]()
218 {
219 avatarEtag.Write(etag);
220 gPrefs->Flush();
221
222 Publish({});
223 });
224 });
225}
226
228{
229 return displayName.Read();
230}
231
233{
234 return userName.Read();
235}
236
238{
239 auto path = MakeAvatarPath();
240
241 if (!wxFileExists(path))
242 return {};
243
244 return path;
245}
246
247} // namespace audacity::cloud::audiocom
Toolkit-neutral facade for basic user interface services.
Declare functions to perform UTF-8 to std::wstring conversions.
Declare an interface for HTTP response.
Declare a class for performing HTTP requests.
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
Declare a class for constructing HTTP requests.
Subscription Subscribe(Callback callback)
Connect a callback to the Publisher; later-connected are called earlier.
Definition: Observer.h:199
CallbackReturn Publish(const UserDataChanged &message)
Send a message to connected callbacks.
Definition: Observer.h:207
A move-only handle representing a connection to a Publisher.
Definition: Observer.h:70
bool Write(const T &value)
Write value to config and return true if successful.
Definition: Prefs.h:259
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:207
Specialization of Setting for strings.
Definition: Prefs.h:370
virtual bool Flush() noexcept=0
Service for providing information about the user profile.
Definition: UserService.h:28
void DownloadAvatar(std::string_view url)
wxString GetUserSlug() const
"Slug" used to construct shareable URLs
void ClearUserData()
Reset the user profile data.
wxString GetAvatarPath() const
Gets a path to the avatar.
wxString GetDisplayName() const
Get the user name to display in the dialog.
void UpdateUserData()
Request the service to update the data.
Definition: UserService.cpp:61
ResponsePtr doGet(const Request &request)
Request & setHeader(const std::string &name, std::string value)
Definition: Request.cpp:46
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:213
FILES_API FilePath ConfigDir()
Audacity user config directory.
UserService & GetUserService()
OAuthService & GetOAuthService()
Returns the instance of the OAuthService.
const ServiceConfig & GetServiceConfig()
Returns the instance of the ServiceConfig.
std::string ToUTF8(const std::wstring &wstr)
wxString ToWXString(const std::string &str)