Opera 12.15 Source Code
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.

additionchecker.cpp 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. /* -*- Mode: c++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*-
  2. *
  3. * Copyright (C) 1995-2012 Opera Software ASA. All rights reserved.
  4. *
  5. * This file is part of the Opera web browser. It may not be distributed
  6. * under any circumstances.
  7. *
  8. * @author Michal Zajaczkowski
  9. */
  10. #include "core/pch.h"
  11. #ifdef AUTO_UPDATE_SUPPORT
  12. #include "adjunct/autoupdate/additionchecker.h"
  13. #include "adjunct/autoupdate/additioncheckerlistener.h"
  14. #include "adjunct/autoupdate/autoupdateserverurl.h"
  15. #include "adjunct/quick/managers/AutoUpdateManager.h"
  16. /**
  17. * The list of updatable resource types that can be resolved by the addition checker.
  18. * No other type of resource will be possible to resolve this way.
  19. */
  20. UpdatableResource::UpdatableResourceType AdditionChecker::KNOWN_TYPES[] =
  21. {
  22. UpdatableResource::RTPlugin,
  23. UpdatableResource::RTDictionary
  24. };
  25. /**
  26. * The number of seconds that will pass between resolving a new addition has been requested and the actual HTTP request.
  27. * The lower this is, the faster the resolve will happen, the higher it is, the less requests will be made since more
  28. * items will have the chance to get scheduled for a single request.
  29. * The plugin install manager might request a couple of mimetypes for a single Web page during the page load, and we'd
  30. * rather send them out with a single HTTP request to the autoupdate server.
  31. */
  32. const unsigned int AdditionChecker::RESOLVE_TIMER_KICK_NOW_PERIOD_SEC = 2;
  33. /**
  34. * The number of seconds that will pass between a failed request to the autoupdate server and a retry attempt. Note that
  35. * requesting any new addition check during the retry time will cause the retry to happen sooner, with the new addition
  36. * resolve attempt.
  37. */
  38. const unsigned int AdditionChecker::RESOLVE_TIMER_KICK_DELAYED_PERDIOD_SEC = 10;
  39. /**
  40. * The maximum number of resolve attempts per item. If the item could not be resolved with this many requests to the autoupdate
  41. * server, it is broadcasted as failing to resolve.
  42. */
  43. const unsigned int AdditionChecker::RESOLVE_MAX_RETRY_COUNT_PER_ITEM = 5;
  44. AdditionChecker::AdditionChecker():
  45. m_resolve_timer(NULL),
  46. m_autoupdate_server_url(NULL),
  47. m_autoupdate_xml(NULL)
  48. {
  49. }
  50. AdditionChecker::~AdditionChecker()
  51. {
  52. OP_DELETE(m_autoupdate_server_url);
  53. OP_DELETE(m_autoupdate_xml);
  54. if (m_resolve_timer)
  55. m_resolve_timer->SetTimerListener(NULL);
  56. OP_DELETE(m_resolve_timer);
  57. // Delete all items that are still not resolved
  58. m_items.DeleteAll();
  59. // Stop and *delete* all downloaders that might happen to be still alive
  60. for (UINT32 i=0; i<m_xml_downloaders.GetCount(); i++)
  61. {
  62. StatusXMLDownloader* downloader = m_xml_downloaders.Get(i);
  63. OP_ASSERT(downloader);
  64. OpStatus::Ignore(downloader->StopRequest());
  65. }
  66. m_xml_downloaders.DeleteAll();
  67. }
  68. OP_STATUS AdditionChecker::Init()
  69. {
  70. OpAutoPtr<OpTimer> resolve_timer_guard;
  71. OpAutoPtr<AutoUpdateXML> autoupdate_xml_guard;
  72. OpAutoPtr<AutoUpdateServerURL> autoupdate_server_url_guard;
  73. m_resolve_timer = OP_NEW(OpTimer, ());
  74. RETURN_OOM_IF_NULL(m_resolve_timer);
  75. resolve_timer_guard = m_resolve_timer;
  76. m_resolve_timer->SetTimerListener(this);
  77. m_autoupdate_xml = OP_NEW(AutoUpdateXML, ());
  78. RETURN_OOM_IF_NULL(m_autoupdate_xml);
  79. autoupdate_xml_guard = m_autoupdate_xml;
  80. RETURN_IF_ERROR(m_autoupdate_xml->Init());
  81. m_autoupdate_server_url = OP_NEW(AutoUpdateServerURL, ());
  82. RETURN_OOM_IF_NULL(m_autoupdate_server_url);
  83. autoupdate_server_url_guard = m_autoupdate_server_url;
  84. RETURN_IF_ERROR(m_autoupdate_server_url->Init());
  85. resolve_timer_guard.release();
  86. autoupdate_xml_guard.release();
  87. autoupdate_server_url_guard.release();
  88. return OpStatus::OK;
  89. }
  90. OP_STATUS AdditionChecker::AddListener(AdditionCheckerListener* listener)
  91. {
  92. return m_listeners.Add(listener);
  93. }
  94. OP_STATUS AdditionChecker::RemoveListener(AdditionCheckerListener* listener)
  95. {
  96. return m_listeners.Remove(listener);
  97. }
  98. OP_STATUS AdditionChecker::CheckForAddition(UpdatableResource::UpdatableResourceType type, const OpStringC& key)
  99. {
  100. // Did you call Init()?
  101. OP_ASSERT(m_resolve_timer);
  102. // Only allow the known types. See AdditionChecker::KNOWN_TYPES[]
  103. if (!IsKnownType(type))
  104. {
  105. OP_ASSERT(!"Uknown resource type passed to addition checker!");
  106. return OpStatus::ERR;
  107. }
  108. // Only allow new items, don't cache same items twice.
  109. if (NULL == GetItem(type, key))
  110. {
  111. // Haven't seen this item before, save it among m_items as pending a resolve attempt.
  112. OpAutoPtr<AdditionCheckerItem> item(OP_NEW(AdditionCheckerItem, ()));
  113. RETURN_OOM_IF_NULL(item.get());
  114. RETURN_IF_ERROR(item->SetKey(key));
  115. item->SetType(type);
  116. RETURN_IF_ERROR(m_items.Add(item.get()));
  117. item.release();
  118. }
  119. /* No matter if the item was known before or not, reset the resolve timer in order to
  120. * shorten the retry time in case we're in the retry period at the moment and still
  121. * allow to cache more items before a request is made if this is a new item.
  122. */
  123. KickResolveTimer(KickNow);
  124. return OpStatus::OK;
  125. }
  126. AdditionCheckerItem* AdditionChecker::GetItem(UpdatableResource::UpdatableResourceType type, const OpStringC& key)
  127. {
  128. OP_ASSERT(key.HasContent());
  129. // Find an item with the given type and key, there should be only one, but we don't verify that here
  130. AdditionCheckerItem* item = NULL;
  131. for (UINT32 i=0; i<m_items.GetCount(); i++)
  132. {
  133. item = m_items.Get(i);
  134. OP_ASSERT(item);
  135. if (item->IsType(type) && item->IsKey(key))
  136. return item;
  137. }
  138. return NULL;
  139. }
  140. OP_STATUS AdditionChecker::GetItemsForDownloader(OpVector<AdditionCheckerItem>& items, StatusXMLDownloader* downloader)
  141. {
  142. // Gather all items that are supposed to be resolved by the given StatusXMLDownloader.
  143. for (UINT32 i=0; i<m_items.GetCount(); i++)
  144. {
  145. AdditionCheckerItem* item = m_items.Get(i);
  146. OP_ASSERT(item);
  147. if (item->IsXMLDownloader(downloader))
  148. RETURN_IF_ERROR(items.Add(item));
  149. }
  150. return OpStatus::OK;
  151. }
  152. OP_STATUS AdditionChecker::GetItemsForDownloaderAndType(OpVector<AdditionCheckerItem>& items, StatusXMLDownloader* downloader, UpdatableResource::UpdatableResourceType type)
  153. {
  154. // Gather all items that are supposed to be resolved by the given StatusXMLDownloader.
  155. for (UINT32 i=0; i<m_items.GetCount(); i++)
  156. {
  157. AdditionCheckerItem* item = m_items.Get(i);
  158. OP_ASSERT(item);
  159. if (item->IsXMLDownloader(downloader) && item->IsType(type))
  160. RETURN_IF_ERROR(items.Add(item));
  161. }
  162. return OpStatus::OK;
  163. }
  164. void AdditionChecker::OnTimeOut(OpTimer* timer)
  165. {
  166. OP_ASSERT(m_resolve_timer);
  167. if (timer == m_resolve_timer)
  168. {
  169. if (OpStatus::IsError(OnResolveTimeOut()))
  170. {
  171. // Something went terribly wrong. Perhaps things will get better next time an addition check is requested,
  172. // however this should not happen.
  173. OP_ASSERT(!"Shouldn't happen");
  174. }
  175. }
  176. else
  177. OP_ASSERT(!"Unknown timer");
  178. }
  179. OP_STATUS AdditionChecker::OnResolveTimeOut()
  180. {
  181. OP_ASSERT(m_resolve_timer);
  182. OP_ASSERT(m_autoupdate_xml);
  183. OP_ASSERT(m_autoupdate_server_url);
  184. // The resolve timer fired. We want to send a request to the autoupdate server asking about all additions that are still not resolved
  185. // at this moment.
  186. // Since the autoupdate server requires a different autoupdate check level for every different addition type and since we really don't
  187. // want to change that, we cannot mix different addition types with one request to the server.
  188. // Go through all the known types
  189. for (UINT32 i=0; i<GetKnownTypesCount(); i++)
  190. {
  191. // The type that we're currently processing
  192. UpdatableResource::UpdatableResourceType current_type = KNOWN_TYPES[i];
  193. OP_ASSERT(current_type);
  194. // Gather a list of all addition items of the current type that are still not resolved in this vector
  195. OpVector<AdditionCheckerItem> items;
  196. // Get a list of all items of the current type that have a NULL downloader, meaning that they are not
  197. // waiting for a callback from a downloader.
  198. RETURN_IF_ERROR(GetItemsForDownloaderAndType(items, NULL, current_type));
  199. if (items.GetCount() > 0)
  200. {
  201. // There is only one AutoUpdateXML, so we need to prepare it for a new request
  202. m_autoupdate_xml->ClearRequestItems();
  203. for (UINT32 i=0; i<items.GetCount(); i++)
  204. {
  205. AdditionCheckerItem* item = items.Get(i);
  206. OP_ASSERT(item);
  207. // Add the item to the AutoUpdateXML, this is needed to generate the proper request XML with all the items
  208. // we want to resolve.
  209. RETURN_IF_ERROR(m_autoupdate_xml->AddRequestItem(item));
  210. }
  211. // Get the current autoupdate server address. It is possible to have multiple addresses in the Autoupdate Server
  212. // preference, these will be cycled through in case of error while getting the response XML.
  213. OpString autoupdate_server_url_string;
  214. RETURN_IF_ERROR(m_autoupdate_server_url->GetCurrentURL(autoupdate_server_url_string));
  215. // Create a new StatusXMLDownloader that will be responsible for resolving this particular list of items
  216. OpAutoPtr<StatusXMLDownloader> current_downloader(OP_NEW(StatusXMLDownloader, ()));
  217. RETURN_OOM_IF_NULL(current_downloader.get());
  218. // The StatusXMLDownloader class saves the response XML to the filesystem in order to parse it. This is needed to
  219. // be able to reparse the XML in case the browser has crashed during autoupdate check, since we want to try hard
  220. // to limit the request count made to the autoupdate server.
  221. // The check type for the downloader determines if the response XML is saved in the autoupdate_response.xml file
  222. // or a temporary file with a temporary name that will be lost right after the check is done.
  223. RETURN_IF_ERROR(current_downloader->Init(StatusXMLDownloader::CheckTypeOther, this));
  224. // Get the autoupdate XML request string basing on the items that we want to resolve
  225. OpString8 xml_string;
  226. RETURN_IF_ERROR(m_autoupdate_xml->GetRequestXML(xml_string));
  227. // Save the StatusXMLDownloader to be able to recoginze it later
  228. RETURN_IF_ERROR(m_xml_downloaders.Add(current_downloader.get()));
  229. // Start the XML request, we'll get a callback via the StatusXMLDownloaderListener interface once the request
  230. // is complete
  231. RETURN_IF_ERROR(current_downloader->StartXMLRequest(autoupdate_server_url_string, xml_string));
  232. // Since everything went fine, we want to set the StatusXMLDownloader pointer for all items that were requested
  233. // with the above call.
  234. for (UINT32 i=0; i<items.GetCount(); i++)
  235. {
  236. AdditionCheckerItem* item = items.Get(i);
  237. OP_ASSERT(item);
  238. item->SetXMLDownloader(current_downloader.get());
  239. }
  240. // Forget about the downloader now, we hold it in the m_xml_downloaders vector
  241. current_downloader.release();
  242. }
  243. else
  244. {
  245. // No items of this type found currently, we'll pick up the remaining items with the next pass.
  246. }
  247. }
  248. return OpStatus::OK;
  249. }
  250. void AdditionChecker::StatusXMLDownloaded(StatusXMLDownloader* downloader)
  251. {
  252. OP_ASSERT(downloader);
  253. // Check if we have spawned the downloader that is calling us back
  254. OP_ASSERT(m_xml_downloaders.Find(downloader) != -1);
  255. // Get all the items that are currently waiting to be resolved by the downloader that is calling us back
  256. OpVector<AdditionCheckerItem> items;
  257. // We can't return an error from this method. The only reason for GetItemsForDownloader() to fail
  258. // seems to be an OOM. Let's try to resume operation with whatever items there are, we need to get
  259. // to deleting the downloader at the end of this method anyway.
  260. OpStatus::Ignore(GetItemsForDownloader(items, downloader));
  261. // The StatusXMLDownloader gives us a list of resources that it got in response to the HTTP request that was sent out.
  262. // First, we check all the items that match both among the items waiting to be resolved and those sent by the downloader.
  263. for (UINT32 i=0; i<items.GetCount(); i++)
  264. {
  265. AdditionCheckerItem* item = items.Get(i);
  266. OP_ASSERT(item);
  267. UpdatableResource* resource = downloader->GetFirstResource();
  268. // Go through all the resources that are currently left in the downloader
  269. while (resource)
  270. {
  271. UpdatableResource::UpdatableResourceType resource_type = resource->GetType();
  272. URAttr resource_key = resource->GetResourceKey();
  273. // You haven't implemented UpdatableResource::GetResourceKey() for your resource type if this assert triggers.
  274. OP_ASSERT(URA_LAST != resource_key);
  275. OpString key_value;
  276. // If this resource is our addition...
  277. if (OpStatus::IsSuccess(resource->GetAttrValue(resource_key, key_value)) && item->IsType(resource_type) && item->IsKey(key_value))
  278. {
  279. // Notify that it is resolved and forget about it
  280. NotifyAdditionResolved(resource_type, key_value, resource);
  281. RETURN_VOID_IF_ERROR(downloader->RemoveResource(resource));
  282. // Note that we have invalid items in the items vector now, but we don't go back over it anyway
  283. RETURN_VOID_IF_ERROR(m_items.Delete(item));
  284. break;
  285. }
  286. resource = downloader->GetNextResource();
  287. }
  288. }
  289. // Go through all the items that we should have gotten with this StatusXMLDownloader but we didn't
  290. items.Clear();
  291. // We can't return an error from this method. The only reason for GetItemsForDownloader() to fail
  292. // seems to be an OOM. Let's try to resume operation with whatever items there are, we need to get
  293. // to deleting the downloader at the end of this method anyway.
  294. OpStatus::Ignore(GetItemsForDownloader(items, downloader));
  295. // This shouldn't really happen since we should always get a response about an item if we ask about it
  296. // but the autoupdate server has its ways.
  297. for (UINT32 i=0; i<items.GetCount(); i++)
  298. {
  299. AdditionCheckerItem* item = items.Get(i);
  300. OP_ASSERT(item);
  301. OpString key;
  302. if (OpStatus::IsSuccess(item->GetKey(key)))
  303. {
  304. // We can only notify if we can know the key.
  305. NotifyAdditionResolveFailed(item->GetType(), key);
  306. }
  307. OpStatus::Ignore(m_items.Delete(item));
  308. }
  309. // Iterate over all the resources that the downloader sent us but that we have never asked for.
  310. UpdatableResource* resource = downloader->GetFirstResource();
  311. while (resource)
  312. {
  313. // If this is a setting then we'll handle it
  314. if (resource->GetType() == UpdatableResource::RTSetting)
  315. {
  316. UpdatableSetting* setting = static_cast<UpdatableSetting*>(resource);
  317. if (!(setting->CheckResource() && OpStatus::IsSuccess(setting->UpdateResource())))
  318. OP_ASSERT(!"Could not update setting resource!");
  319. }
  320. else
  321. {
  322. // But if it's anything else then we're really confused
  323. OP_ASSERT(!"Unexpected resource got from the autoupdate server!");
  324. }
  325. OpStatus::Ignore(downloader->RemoveResource(resource));
  326. resource = downloader->GetNextResource();
  327. }
  328. // Delete the downloader finally.
  329. OpStatus::Ignore(m_xml_downloaders.Delete(downloader));
  330. }
  331. void AdditionChecker::StatusXMLDownloadFailed(StatusXMLDownloader* downloader, StatusXMLDownloader::DownloadStatus status)
  332. {
  333. OP_ASSERT(downloader);
  334. // Check if we know the downloader
  335. OP_ASSERT(m_xml_downloaders.Find(downloader) != -1);
  336. // Try to increment the server URL, i.e. use a different one next time. This will most likely fail since we use one server anyway.
  337. OpStatus::Ignore(m_autoupdate_server_url->IncrementURLNo(AutoUpdateServerURL::Wrap));
  338. // Get the list of all the items that should be resolved by this downloader
  339. OpVector<AdditionCheckerItem> items;
  340. // We can't return an error from this method. The only reason for GetItemsForDownloader() to fail
  341. // seems to be an OOM. Let's try to resume operation with whatever items there are, we need to get
  342. // to deleting the downloader at the end of this method anyway.
  343. OpStatus::Ignore(GetItemsForDownloader(items, downloader));
  344. for (UINT32 i=0; i<items.GetCount(); i++)
  345. {
  346. AdditionCheckerItem* item = items.Get(i);
  347. OP_ASSERT(item);
  348. // Increment the retry timer
  349. item->IncRetryCounter();
  350. // Set the downloader to NULL in order to pick this item up with the next resolve attempt.
  351. item->SetXMLDownloader(NULL);
  352. // However in case the item has reached the maximum retry count, notify it has failed to be resolved and drop it without a further retry
  353. if (item->GetRetryCounterValue() >= RESOLVE_MAX_RETRY_COUNT_PER_ITEM)
  354. {
  355. OpString item_key;
  356. UpdatableResource::UpdatableResourceType item_type = item->GetType();
  357. if (OpStatus::IsSuccess(item->GetKey(item_key)))
  358. NotifyAdditionResolveFailed(item_type, item_key);
  359. OpStatus::Ignore(m_items.Delete(item));
  360. }
  361. }
  362. // If there still are some items that should be retried, schedule a retry attempt
  363. if (items.GetCount() > 0)
  364. KickResolveTimer(KickDelayed);
  365. OpStatus::Ignore(m_xml_downloaders.Delete(downloader));
  366. }
  367. void AdditionChecker::NotifyAdditionResolved(UpdatableResource::UpdatableResourceType type, const OpStringC& key, UpdatableResource* resource)
  368. {
  369. for (OpListenersIterator iterator(m_listeners); m_listeners.HasNext(iterator);)
  370. m_listeners.GetNext(iterator)->OnAdditionResolved(type, key, resource);
  371. }
  372. void AdditionChecker::NotifyAdditionResolveFailed(UpdatableResource::UpdatableResourceType type, const OpStringC& key)
  373. {
  374. for (OpListenersIterator iterator(m_listeners); m_listeners.HasNext(iterator);)
  375. m_listeners.GetNext(iterator)->OnAdditionResolveFailed(type, key);
  376. }
  377. UINT32 AdditionChecker::GetKnownTypesCount()
  378. {
  379. size_t item_size = sizeof(UpdatableResource::UpdatableResourceType);
  380. size_t full_size = sizeof(KNOWN_TYPES);
  381. return full_size / item_size;
  382. }
  383. bool AdditionChecker::IsKnownType(UpdatableResource::UpdatableResourceType type)
  384. {
  385. UINT32 count = GetKnownTypesCount();
  386. for (UINT32 i=0; i<count; i++)
  387. {
  388. if (KNOWN_TYPES[i] == type)
  389. return true;
  390. }
  391. return false;
  392. }
  393. void AdditionChecker::KickResolveTimer(KickType type)
  394. {
  395. OP_ASSERT(m_resolve_timer);
  396. if (KickNow == type)
  397. m_resolve_timer->Start(RESOLVE_TIMER_KICK_NOW_PERIOD_SEC * 1000);
  398. else
  399. m_resolve_timer->Start(RESOLVE_TIMER_KICK_DELAYED_PERDIOD_SEC * 1000);
  400. }
  401. /***********************
  402. * AdditionCheckerItem *
  403. ***********************/
  404. AdditionCheckerItem::AdditionCheckerItem():
  405. m_status_xml_downloader(NULL),
  406. m_type(UpdatableResource::RTEmpty),
  407. m_retry_counter(0)
  408. {
  409. }
  410. AdditionCheckerItem::~AdditionCheckerItem()
  411. {
  412. }
  413. OP_STATUS AdditionCheckerItem::SetKey(const OpStringC& key)
  414. {
  415. return m_key.Set(key);
  416. }
  417. void AdditionCheckerItem::SetType(UpdatableResource::UpdatableResourceType type)
  418. {
  419. m_type = type;
  420. }
  421. OP_STATUS AdditionCheckerItem::GetKey(OpString& key)
  422. {
  423. return key.Set(m_key);
  424. }
  425. UpdatableResource::UpdatableResourceType AdditionCheckerItem::GetType()
  426. {
  427. return m_type;
  428. }
  429. bool AdditionCheckerItem::IsType(UpdatableResource::UpdatableResourceType type)
  430. {
  431. return m_type == type;
  432. }
  433. bool AdditionCheckerItem::IsKey(const OpStringC& key)
  434. {
  435. return m_key.CompareI(key) == 0;
  436. }
  437. bool AdditionCheckerItem::IsXMLDownloader(const StatusXMLDownloader* downloader)
  438. {
  439. return m_status_xml_downloader == downloader;
  440. }
  441. void AdditionCheckerItem::SetXMLDownloader(const StatusXMLDownloader* downloader)
  442. {
  443. m_status_xml_downloader = downloader;
  444. }
  445. const StatusXMLDownloader* AdditionCheckerItem::GetXMLDownloader()
  446. {
  447. return m_status_xml_downloader;
  448. }
  449. #endif // AUTO_UPDATE_SUPPORT