| OLD | NEW |
| (Empty) |
| 1 // Copyright 2010 Google Inc. | |
| 2 // | |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 // you may not use this file except in compliance with the License. | |
| 5 // You may obtain a copy of the License at | |
| 6 // | |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 // | |
| 9 // Unless required by applicable law or agreed to in writing, software | |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 // See the License for the specific language governing permissions and | |
| 13 // limitations under the License. | |
| 14 // ======================================================================== | |
| 15 | |
| 16 #include "omaha/goopdate/update_response_utils.h" | |
| 17 #include "omaha/base/debug.h" | |
| 18 #include "omaha/base/error.h" | |
| 19 #include "omaha/base/logging.h" | |
| 20 #include "omaha/common/lang.h" | |
| 21 #include "omaha/common/experiment_labels.h" | |
| 22 #include "omaha/goopdate/model.h" | |
| 23 #include "omaha/goopdate/server_resource.h" | |
| 24 #include "omaha/goopdate/string_formatter.h" | |
| 25 | |
| 26 namespace omaha { | |
| 27 | |
| 28 namespace update_response_utils { | |
| 29 | |
| 30 // TODO(omaha): unit test the functions below. | |
| 31 | |
| 32 // Returns a pointer to the app object corresponding to the appid in the | |
| 33 // response object. Returns NULL if the app is not found. | |
| 34 const xml::response::App* GetApp(const xml::response::Response& response, | |
| 35 const CString& appid) { | |
| 36 size_t app_index = 0; | |
| 37 for (; app_index < response.apps.size(); ++app_index) { | |
| 38 if (!appid.CompareNoCase(response.apps[app_index].appid)) { | |
| 39 return &response.apps[app_index]; | |
| 40 } | |
| 41 } | |
| 42 return NULL; | |
| 43 } | |
| 44 | |
| 45 HRESULT GetInstallData(const std::vector<xml::response::Data>& data, | |
| 46 const CString& install_data_index, | |
| 47 CString* install_data) { | |
| 48 ASSERT1(install_data); | |
| 49 if (install_data_index.IsEmpty()) { | |
| 50 return S_OK; | |
| 51 } | |
| 52 | |
| 53 std::vector<xml::response::Data>::const_iterator it; | |
| 54 for (it = data.begin(); it != data.end(); ++it) { | |
| 55 if (install_data_index != it->install_data_index) { | |
| 56 continue; | |
| 57 } | |
| 58 | |
| 59 if (it->status != kResponseStatusOkValue) { | |
| 60 ASSERT1(it->status == kResponseDataStatusNoData); | |
| 61 return GOOPDATE_E_INVALID_INSTALL_DATA_INDEX; | |
| 62 } | |
| 63 | |
| 64 ASSERT1(!it->install_data.IsEmpty()); | |
| 65 *install_data = it->install_data; | |
| 66 return S_OK; | |
| 67 } | |
| 68 | |
| 69 return GOOPDATE_E_INVALID_INSTALL_DATA_INDEX; | |
| 70 } | |
| 71 | |
| 72 // Check the outer elements first, then check any child elements only if the | |
| 73 // outer element was successful. | |
| 74 CString GetAppResponseStatus(const xml::response::App& app) { | |
| 75 if (_tcsicmp(kResponseStatusOkValue, app.status) != 0) { | |
| 76 ASSERT1(!app.status.IsEmpty()); | |
| 77 return app.status; | |
| 78 } | |
| 79 | |
| 80 if (!app.update_check.status.IsEmpty() && | |
| 81 _tcsicmp(kResponseStatusOkValue, app.update_check.status) != 0) { | |
| 82 return app.update_check.status; | |
| 83 } | |
| 84 | |
| 85 std::vector<xml::response::Data>::const_iterator data; | |
| 86 for (data = app.data.begin(); data != app.data.end(); ++data) { | |
| 87 if (!data->status.IsEmpty() && | |
| 88 _tcsicmp(kResponseStatusOkValue, data->status) != 0) { | |
| 89 return data->status; | |
| 90 } | |
| 91 } | |
| 92 | |
| 93 if (!app.ping.status.IsEmpty() && | |
| 94 _tcsicmp(kResponseStatusOkValue, app.ping.status) != 0) { | |
| 95 return app.ping.status; | |
| 96 } | |
| 97 | |
| 98 std::vector<xml::response::Event>::const_iterator it; | |
| 99 for (it = app.events.begin(); it != app.events.end(); ++it) { | |
| 100 if (!it->status.IsEmpty() && | |
| 101 _tcsicmp(kResponseStatusOkValue, it->status) != 0) { | |
| 102 return it->status; | |
| 103 } | |
| 104 } | |
| 105 | |
| 106 // TODO(omaha): verify that no other elements can report errors | |
| 107 // once we've finalized the protocol. | |
| 108 | |
| 109 return app.status; | |
| 110 } | |
| 111 | |
| 112 HRESULT BuildApp(const xml::UpdateResponse* update_response, | |
| 113 HRESULT code, | |
| 114 App* app) { | |
| 115 ASSERT1(update_response); | |
| 116 ASSERT1(SUCCEEDED(code) || code == GOOPDATE_E_NO_UPDATE_RESPONSE); | |
| 117 ASSERT1(app); | |
| 118 | |
| 119 AppVersion* next_version = app->next_version(); | |
| 120 | |
| 121 const CString& app_id = app->app_guid_string(); | |
| 122 | |
| 123 const xml::response::App* response_app(GetApp(update_response->response(), | |
| 124 app_id)); | |
| 125 ASSERT1(response_app); | |
| 126 const xml::response::UpdateCheck& update_check = response_app->update_check; | |
| 127 | |
| 128 VERIFY1(SUCCEEDED(app->put_ttToken(CComBSTR(update_check.tt_token)))); | |
| 129 | |
| 130 if (code == GOOPDATE_E_NO_UPDATE_RESPONSE) { | |
| 131 return S_OK; | |
| 132 } | |
| 133 | |
| 134 for (size_t i = 0; i < update_check.urls.size(); ++i) { | |
| 135 HRESULT hr = next_version->AddDownloadBaseUrl(update_check.urls[i]); | |
| 136 if (FAILED(hr)) { | |
| 137 return hr; | |
| 138 } | |
| 139 } | |
| 140 | |
| 141 for (size_t i = 0; i < update_check.install_manifest.packages.size(); ++i) { | |
| 142 const xml::InstallPackage& package( | |
| 143 update_check.install_manifest.packages[i]); | |
| 144 HRESULT hr = next_version->AddPackage(package.name, | |
| 145 package.size, | |
| 146 package.hash); | |
| 147 if (FAILED(hr)) { | |
| 148 return hr; | |
| 149 } | |
| 150 } | |
| 151 | |
| 152 CString server_install_data; | |
| 153 HRESULT hr = GetInstallData(response_app->data, | |
| 154 app->server_install_data_index(), | |
| 155 &server_install_data); | |
| 156 if (FAILED(hr)) { | |
| 157 return hr; | |
| 158 } | |
| 159 app->set_server_install_data(server_install_data); | |
| 160 | |
| 161 ASSERT1(!next_version->install_manifest()); | |
| 162 next_version->set_install_manifest( | |
| 163 new xml::InstallManifest(update_check.install_manifest)); | |
| 164 | |
| 165 // TODO(omaha): it appears the version_ below holds either the manifest | |
| 166 // version or the "pv" version, written by the installer. If this is the case, | |
| 167 // then it is confusing and perhaps we need to have two different members to | |
| 168 // hold these values. | |
| 169 ASSERT1(next_version->version().IsEmpty()); | |
| 170 next_version->set_version(next_version->install_manifest()->version); | |
| 171 | |
| 172 return S_OK; | |
| 173 } | |
| 174 | |
| 175 // "noupdate" is an error for fresh installs, but a successful completion in | |
| 176 // the cases of silent and on demand updates. The caller is responsible for | |
| 177 // interpreting "noupdate" as it sees fit. | |
| 178 xml::UpdateResponseResult GetResult(const xml::UpdateResponse* update_response, | |
| 179 const CString& appid, | |
| 180 const CString& language) { | |
| 181 ASSERT1(update_response); | |
| 182 const xml::response::App* response_app(GetApp(update_response->response(), | |
| 183 appid)); | |
| 184 | |
| 185 StringFormatter formatter(language); | |
| 186 CString text; | |
| 187 | |
| 188 if (!response_app) { | |
| 189 CORE_LOG(L1, (_T("[UpdateResponse::GetResult][app not found][%s]"), appid)); | |
| 190 VERIFY1(SUCCEEDED(formatter.LoadString(IDS_UNKNOWN_APPLICATION, &text))); | |
| 191 return std::make_pair(GOOPDATE_E_NO_SERVER_RESPONSE, text); | |
| 192 } | |
| 193 | |
| 194 const xml::response::UpdateCheck& update_check = response_app->update_check; | |
| 195 const CString& status = GetAppResponseStatus(*response_app); | |
| 196 const CString& display_name = update_check.install_manifest.name; | |
| 197 | |
| 198 ASSERT1(!status.IsEmpty()); | |
| 199 CORE_LOG(L1, (_T("[UpdateResponse::GetResult][%s][%s][%s]"), | |
| 200 appid, status, display_name)); | |
| 201 | |
| 202 // ok | |
| 203 if (_tcsicmp(kResponseStatusOkValue, status) == 0) { | |
| 204 return std::make_pair(S_OK, CString()); | |
| 205 } | |
| 206 | |
| 207 // noupdate | |
| 208 if (_tcsicmp(kResponseStatusNoUpdate, status) == 0) { | |
| 209 VERIFY1(SUCCEEDED(formatter.LoadString(IDS_NO_UPDATE_RESPONSE, &text))); | |
| 210 return std::make_pair(GOOPDATE_E_NO_UPDATE_RESPONSE, text); | |
| 211 } | |
| 212 | |
| 213 // "restricted" | |
| 214 if (_tcsicmp(kResponseStatusRestrictedExportCountry, status) == 0) { | |
| 215 VERIFY1(SUCCEEDED(formatter.LoadString(IDS_RESTRICTED_RESPONSE_FROM_SERVER, | |
| 216 &text))); | |
| 217 return std::make_pair(GOOPDATE_E_RESTRICTED_SERVER_RESPONSE, text); | |
| 218 } | |
| 219 | |
| 220 // "error-UnKnownApplication" | |
| 221 if (_tcsicmp(kResponseStatusUnKnownApplication, status) == 0) { | |
| 222 VERIFY1(SUCCEEDED(formatter.LoadString(IDS_UNKNOWN_APPLICATION, &text))); | |
| 223 return std::make_pair(GOOPDATE_E_UNKNOWN_APP_SERVER_RESPONSE, text); | |
| 224 } | |
| 225 | |
| 226 // "error-OsNotSupported" | |
| 227 if (_tcsicmp(kResponseStatusOsNotSupported, status) == 0) { | |
| 228 const CString& error_url(update_check.error_url); | |
| 229 VERIFY1(SUCCEEDED(formatter.LoadString(IDS_OS_NOT_SUPPORTED, &text))); | |
| 230 if (!error_url.IsEmpty()) { | |
| 231 // TODO(omaha3): The error URL is no longer in the error string. Either | |
| 232 // we need to provide this URL to the client or we need to deprecate | |
| 233 // error_url and put this information in the Get Help redirect. | |
| 234 // Alternatively, we could have the COM server build error URLs in all | |
| 235 // cases. The current UI would still need to build an URL for the entire | |
| 236 // bundle, though, because it does not have per-app links. | |
| 237 } | |
| 238 return std::make_pair(GOOPDATE_E_OS_NOT_SUPPORTED, text); | |
| 239 } | |
| 240 | |
| 241 // "error-internal" | |
| 242 if (_tcsicmp(kResponseStatusInternalError, status) == 0) { | |
| 243 VERIFY1(SUCCEEDED(formatter.FormatMessage(&text, | |
| 244 IDS_NON_OK_RESPONSE_FROM_SERVER, | |
| 245 status))); | |
| 246 return std::make_pair(GOOPDATE_E_INTERNAL_ERROR_SERVER_RESPONSE, text); | |
| 247 } | |
| 248 | |
| 249 // "error-hash" | |
| 250 if (_tcsicmp(kResponseStatusHashError, status) == 0) { | |
| 251 VERIFY1(SUCCEEDED(formatter.FormatMessage(&text, | |
| 252 IDS_NON_OK_RESPONSE_FROM_SERVER, | |
| 253 status))); | |
| 254 return std::make_pair(GOOPDATE_E_SERVER_RESPONSE_NO_HASH, text); | |
| 255 } | |
| 256 | |
| 257 // "error-unsupportedprotocol" | |
| 258 if (_tcsicmp(kResponseStatusUnsupportedProtocol, status) == 0) { | |
| 259 // TODO(omaha): Ideally, we would provide an app-specific URL instead of | |
| 260 // just the publisher name. If it was a link, we could use point to a | |
| 261 // redirect URL and provide the app GUID rather than somehow obtaining the | |
| 262 // app-specific URL. | |
| 263 VERIFY1(SUCCEEDED(formatter.FormatMessage(&text, | |
| 264 IDS_INSTALLER_OLD, | |
| 265 kShortCompanyName))); | |
| 266 return std::make_pair(GOOPDATE_E_SERVER_RESPONSE_UNSUPPORTED_PROTOCOL, | |
| 267 text); | |
| 268 } | |
| 269 | |
| 270 VERIFY1(SUCCEEDED(formatter.FormatMessage(&text, | |
| 271 IDS_NON_OK_RESPONSE_FROM_SERVER, | |
| 272 status))); | |
| 273 return std::make_pair(GOOPDATE_E_UNKNOWN_SERVER_RESPONSE, text); | |
| 274 } | |
| 275 | |
| 276 bool IsOmahaUpdateAvailable(const xml::UpdateResponse* update_response) { | |
| 277 ASSERT1(update_response); | |
| 278 xml::UpdateResponseResult update_response_result( | |
| 279 update_response_utils::GetResult(update_response, | |
| 280 kGoogleUpdateAppId, | |
| 281 lang::GetDefaultLanguage(true))); | |
| 282 return update_response_result.first == S_OK; | |
| 283 } | |
| 284 | |
| 285 HRESULT ApplyExperimentLabelDeltas(bool is_machine, | |
| 286 const xml::UpdateResponse* update_response) { | |
| 287 ASSERT1(update_response); | |
| 288 | |
| 289 for (size_t app_index = 0; | |
| 290 app_index < update_response->response().apps.size(); | |
| 291 ++app_index) { | |
| 292 const xml::response::App& app = update_response->response().apps[app_index]; | |
| 293 if (!app.experiments.IsEmpty()) { | |
| 294 VERIFY1(IsGuid(app.appid)); | |
| 295 | |
| 296 ExperimentLabels labels; | |
| 297 VERIFY1(SUCCEEDED(labels.ReadFromRegistry(is_machine, app.appid))); | |
| 298 | |
| 299 if (!labels.DeserializeAndApplyDelta(app.experiments)) { | |
| 300 return E_FAIL; | |
| 301 } | |
| 302 | |
| 303 HRESULT hr = labels.WriteToRegistry(is_machine, app.appid); | |
| 304 if (FAILED(hr)) { | |
| 305 return hr; | |
| 306 } | |
| 307 } | |
| 308 } | |
| 309 | |
| 310 return S_OK; | |
| 311 } | |
| 312 | |
| 313 } // namespace update_response_utils | |
| 314 | |
| 315 } // namespace omaha | |
| 316 | |
| OLD | NEW |