1
0

Cloud.cpp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. #include "main.h"
  2. #include "api__ml_wire.h"
  3. #include "Cloud.h"
  4. #include "FeedParse.h"
  5. #include "Defaults.h"
  6. #include "./subscriptionView.h"
  7. #include "ChannelRefresher.h"
  8. #include "Util.h"
  9. #include <algorithm>
  10. #include <strsafe.h>
  11. /* benski> TODO rewrite Callback() so we don't have to reserve a thread */
  12. using namespace Nullsoft::Utility;
  13. #define CLOUD_TICK_MS 60000
  14. ChannelRefresher channelRefresher;
  15. static bool kill = false;
  16. static __time64_t RetrieveMinimalUpdateTime()
  17. {
  18. __time64_t minUpdateTime = 0;
  19. AutoLock lock (channels LOCKNAME("RetrieveMinimalUpdateTime"));
  20. ChannelList::iterator itr;
  21. for (itr=channels.begin(); itr!=channels.end(); itr++)
  22. {
  23. if (itr->useDefaultUpdate)
  24. {
  25. if ( !updateTime ) autoUpdate = 0;
  26. if ( autoUpdate && (!minUpdateTime || minUpdateTime && (updateTime < minUpdateTime)) )
  27. {
  28. minUpdateTime = updateTime;
  29. }
  30. }
  31. else // use the custom values
  32. {
  33. if ( !itr->updateTime ) itr->autoUpdate = 0;
  34. if ( itr->autoUpdate && (!minUpdateTime || minUpdateTime && (itr->updateTime < minUpdateTime)) )
  35. {
  36. minUpdateTime = itr->updateTime;
  37. }
  38. }
  39. }
  40. return minUpdateTime;
  41. }
  42. int Cloud::CloudThreadPoolFunc(HANDLE handle, void *user_data, intptr_t id)
  43. {
  44. Cloud *cloud = (Cloud *)user_data;
  45. if (kill)
  46. {
  47. WASABI_API_THREADPOOL->RemoveHandle(0, cloud->cloudEvent);
  48. CloseHandle(cloud->cloudEvent);
  49. WASABI_API_THREADPOOL->RemoveHandle(0, cloud->cloudTimerEvent);
  50. cloud->cloudTimerEvent.Close();
  51. SetEvent(cloud->cloudDone);
  52. return 0;
  53. }
  54. cloud->Callback();
  55. // set waitable timer, overwrite previouse value if any
  56. if (!kill)
  57. {
  58. __time64_t timeToWait = RetrieveMinimalUpdateTime();
  59. if ( timeToWait )
  60. cloud->cloudTimerEvent.Wait(timeToWait * 1000);
  61. }
  62. return 0;
  63. }
  64. Cloud::Cloud() : cloudThread(0), cloudEvent(0), statusText(0)
  65. {}
  66. Cloud::~Cloud()
  67. {
  68. free(statusText);
  69. }
  70. void Cloud::Quit()
  71. {
  72. cloudDone= CreateEvent(NULL, FALSE, FALSE, NULL);
  73. kill = true;
  74. SetEvent(cloudEvent);
  75. WaitForSingleObject(cloudDone, INFINITE);
  76. CloseHandle(cloudDone);
  77. }
  78. void Cloud::RefreshAll()
  79. {
  80. AutoLock lock (channels LOCKNAME("RefreshAll"));
  81. ChannelList::iterator itr;
  82. for (itr = channels.begin();itr != channels.end();itr++)
  83. {
  84. itr->needsRefresh = true;
  85. }
  86. }
  87. void Cloud::Init()
  88. {
  89. // setup a periodic callback so we can check on our times
  90. cloudEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  91. cloudThread = WASABI_API_THREADPOOL->ReserveThread(0);
  92. WASABI_API_THREADPOOL->AddHandle(cloudThread, cloudEvent, CloudThreadPoolFunc, this, 0, 0);
  93. WASABI_API_THREADPOOL->AddHandle(cloudThread, cloudTimerEvent, CloudThreadPoolFunc, this, 1, 0);
  94. __time64_t timeToWait = RetrieveMinimalUpdateTime();
  95. if ( timeToWait )
  96. cloudTimerEvent.Wait(timeToWait * 1000);
  97. }
  98. void Cloud::Refresh(Channel &channel)
  99. {
  100. wchar_t lang_buf[1024] = {0};
  101. WASABI_API_LNGSTRINGW_BUF(IDS_RECEIVING_UPDATES_FOR, lang_buf, 1024);
  102. if (channel.title)
  103. StringCbCat(lang_buf, sizeof(lang_buf), channel.title);
  104. else
  105. lang_buf[0]=0;
  106. SetStatus(lang_buf);
  107. size_t oldSize = channel.items.size();
  108. FeedParse downloader(&channelRefresher, false);
  109. downloader.DownloadURL(channel.url);
  110. if (channel.items.size() > oldSize)
  111. {
  112. WASABI_API_LNGSTRINGW_BUF(IDS_GOT_NEW_ITEMS_FOR, lang_buf, 1024);
  113. StringCbCat(lang_buf, sizeof(lang_buf), channel.title);
  114. SetStatus(lang_buf);
  115. }
  116. else
  117. SetStatus(L"");
  118. }
  119. void Cloud::GetStatus(wchar_t *status, size_t len)
  120. {
  121. AutoLock lock (statusGuard);
  122. if (statusText)
  123. StringCchCopy(status, len, statusText);
  124. else
  125. status[0]=0;
  126. }
  127. void Cloud::SetStatus(const wchar_t *newStatus)
  128. {
  129. AutoLock lock (statusGuard);
  130. free(statusText);
  131. statusText = _wcsdup(newStatus);
  132. HWND hView = SubscriptionView_FindWindow();
  133. if (NULL != hView)
  134. SubscriptionView_SetStatus(hView, statusText);
  135. }
  136. /* --- Private Methods of class Cloud --- */
  137. static void ForceLastUpdate(const Channel &channel)
  138. {
  139. AutoLock lock (channels LOCKNAME("ChannelRefresher::NewChannel"));
  140. ChannelList::iterator found;
  141. for (found=channels.begin();found!=channels.end(); found++)
  142. {
  143. if (!wcscmp(found->url, channel.url))
  144. break;
  145. }
  146. if (found != channels.end())
  147. {
  148. found->lastUpdate = _time64(0);
  149. found->needsRefresh = false;
  150. }
  151. }
  152. /*
  153. @private
  154. checks all channels and updates any that requiring refreshing.
  155. */
  156. void Cloud::Callback()
  157. {
  158. __time64_t curTime = _time64(0);
  159. size_t i = 0;
  160. Channel temp;
  161. bool refreshed = false;
  162. while (true) // we need to lock the channels object before we check its size, etc, so we can't just use a "for" loop.
  163. {
  164. { // we want to minimize how long we have to lock, so we'll make a copy of the channel data
  165. AutoLock lock (channels LOCKNAME("Callback"));
  166. if (i >= channels.size())
  167. break;
  168. temp = channels[i]; // make a copy the data so we can safely release the lock
  169. channels[i].needsRefresh = false; // have to set this now. if the site is down or 404, then the refresh will never "complete".
  170. } // end locking scope
  171. if (temp.needsRefresh) // need an immediate refresh? (usually set when the user clicks refresh or update-on-launch is on)
  172. {
  173. Refresh(temp);
  174. refreshed = true;
  175. }
  176. else if (temp.useDefaultUpdate) // this flag is set unless the user chose custom update values
  177. {
  178. if (!updateTime) autoUpdate = 0;
  179. if (autoUpdate && (temp.lastUpdate + updateTime) <= curTime)
  180. {
  181. Refresh(temp);
  182. ForceLastUpdate(temp);
  183. refreshed = true;
  184. }
  185. }
  186. else // use the custom values
  187. {
  188. if (temp.updateTime == 0) temp.autoUpdate = 0;
  189. if (temp.autoUpdate && (temp.lastUpdate + temp.updateTime) <= curTime)
  190. {
  191. Refresh(temp);
  192. ForceLastUpdate(temp);
  193. refreshed = true;
  194. }
  195. }
  196. i++;
  197. }
  198. // if we're refreshing then save out
  199. if (refreshed)
  200. {
  201. SaveAll();
  202. }
  203. }