CaptivePortalService.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. #include "mozilla/net/CaptivePortalService.h"
  5. #include "mozilla/Services.h"
  6. #include "mozilla/Preferences.h"
  7. #include "nsIObserverService.h"
  8. #include "nsServiceManagerUtils.h"
  9. #include "nsXULAppAPI.h"
  10. static const char16_t kInterfaceName[] = u"captive-portal-inteface";
  11. static const char kOpenCaptivePortalLoginEvent[] = "captive-portal-login";
  12. static const char kAbortCaptivePortalLoginEvent[] = "captive-portal-login-abort";
  13. static const char kCaptivePortalLoginSuccessEvent[] = "captive-portal-login-success";
  14. static const uint32_t kDefaultInterval = 60*1000; // check every 60 seconds
  15. namespace mozilla {
  16. namespace net {
  17. static LazyLogModule gCaptivePortalLog("CaptivePortalService");
  18. #undef LOG
  19. #define LOG(args) MOZ_LOG(gCaptivePortalLog, mozilla::LogLevel::Debug, args)
  20. NS_IMPL_ISUPPORTS(CaptivePortalService, nsICaptivePortalService, nsIObserver,
  21. nsISupportsWeakReference, nsITimerCallback,
  22. nsICaptivePortalCallback)
  23. CaptivePortalService::CaptivePortalService()
  24. : mState(UNKNOWN)
  25. , mStarted(false)
  26. , mInitialized(false)
  27. , mRequestInProgress(false)
  28. , mEverBeenCaptive(false)
  29. , mDelay(kDefaultInterval)
  30. , mSlackCount(0)
  31. , mMinInterval(kDefaultInterval)
  32. , mMaxInterval(25*kDefaultInterval)
  33. , mBackoffFactor(5.0)
  34. {
  35. mLastChecked = TimeStamp::Now();
  36. }
  37. CaptivePortalService::~CaptivePortalService()
  38. {
  39. LOG(("CaptivePortalService::~CaptivePortalService isParentProcess:%d\n",
  40. XRE_GetProcessType() == GeckoProcessType_Default));
  41. }
  42. nsresult
  43. CaptivePortalService::PerformCheck()
  44. {
  45. LOG(("CaptivePortalService::PerformCheck mRequestInProgress:%d mInitialized:%d mStarted:%d\n",
  46. mRequestInProgress, mInitialized, mStarted));
  47. // Don't issue another request if last one didn't complete
  48. if (mRequestInProgress || !mInitialized || !mStarted) {
  49. return NS_OK;
  50. }
  51. MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
  52. nsresult rv;
  53. if (!mCaptivePortalDetector) {
  54. mCaptivePortalDetector =
  55. do_GetService("@mozilla.org/toolkit/captive-detector;1", &rv);
  56. if (NS_FAILED(rv)) {
  57. LOG(("Unable to get a captive portal detector\n"));
  58. return rv;
  59. }
  60. }
  61. LOG(("CaptivePortalService::PerformCheck - Calling CheckCaptivePortal\n"));
  62. mRequestInProgress = true;
  63. mCaptivePortalDetector->CheckCaptivePortal(kInterfaceName, this);
  64. return NS_OK;
  65. }
  66. nsresult
  67. CaptivePortalService::RearmTimer()
  68. {
  69. LOG(("CaptivePortalService::RearmTimer\n"));
  70. // Start a timer to recheck
  71. MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
  72. if (mTimer) {
  73. mTimer->Cancel();
  74. }
  75. // If we have successfully determined the state, and we have never detected
  76. // a captive portal, we don't need to keep polling, but will rely on events
  77. // to trigger detection.
  78. if (mState == NOT_CAPTIVE) {
  79. return NS_OK;
  80. }
  81. if (!mTimer) {
  82. mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
  83. }
  84. if (mTimer && mDelay > 0) {
  85. LOG(("CaptivePortalService - Reloading timer with delay %u\n", mDelay));
  86. return mTimer->InitWithCallback(this, mDelay, nsITimer::TYPE_ONE_SHOT);
  87. }
  88. return NS_OK;
  89. }
  90. nsresult
  91. CaptivePortalService::Initialize()
  92. {
  93. if (mInitialized) {
  94. return NS_OK;
  95. }
  96. mInitialized = true;
  97. // Only the main process service should actually do anything. The service in
  98. // the content process only mirrors the CP state in the main process.
  99. if (XRE_GetProcessType() != GeckoProcessType_Default) {
  100. return NS_OK;
  101. }
  102. nsCOMPtr<nsIObserverService> observerService =
  103. mozilla::services::GetObserverService();
  104. if (observerService) {
  105. observerService->AddObserver(this, kOpenCaptivePortalLoginEvent, true);
  106. observerService->AddObserver(this, kAbortCaptivePortalLoginEvent, true);
  107. observerService->AddObserver(this, kCaptivePortalLoginSuccessEvent, true);
  108. }
  109. LOG(("Initialized CaptivePortalService\n"));
  110. return NS_OK;
  111. }
  112. nsresult
  113. CaptivePortalService::Start()
  114. {
  115. if (!mInitialized) {
  116. return NS_ERROR_NOT_INITIALIZED;
  117. }
  118. if (XRE_GetProcessType() != GeckoProcessType_Default) {
  119. // Doesn't do anything if called in the content process.
  120. return NS_OK;
  121. }
  122. if (mStarted) {
  123. return NS_OK;
  124. }
  125. MOZ_ASSERT(mState == UNKNOWN, "Initial state should be UNKNOWN");
  126. mStarted = true;
  127. mEverBeenCaptive = false;
  128. // Get the delay prefs
  129. Preferences::GetUint("network.captive-portal-service.minInterval", &mMinInterval);
  130. Preferences::GetUint("network.captive-portal-service.maxInterval", &mMaxInterval);
  131. Preferences::GetFloat("network.captive-portal-service.backoffFactor", &mBackoffFactor);
  132. LOG(("CaptivePortalService::Start min:%u max:%u backoff:%.2f\n",
  133. mMinInterval, mMaxInterval, mBackoffFactor));
  134. mSlackCount = 0;
  135. mDelay = mMinInterval;
  136. // When Start is called, perform a check immediately
  137. PerformCheck();
  138. RearmTimer();
  139. return NS_OK;
  140. }
  141. nsresult
  142. CaptivePortalService::Stop()
  143. {
  144. LOG(("CaptivePortalService::Stop\n"));
  145. if (XRE_GetProcessType() != GeckoProcessType_Default) {
  146. // Doesn't do anything when called in the content process.
  147. return NS_OK;
  148. }
  149. if (!mStarted) {
  150. return NS_OK;
  151. }
  152. if (mTimer) {
  153. mTimer->Cancel();
  154. }
  155. mTimer = nullptr;
  156. mRequestInProgress = false;
  157. mStarted = false;
  158. if (mCaptivePortalDetector) {
  159. mCaptivePortalDetector->Abort(kInterfaceName);
  160. }
  161. mCaptivePortalDetector = nullptr;
  162. // Clear the state in case anyone queries the state while detection is off.
  163. mState = UNKNOWN;
  164. return NS_OK;
  165. }
  166. void
  167. CaptivePortalService::SetStateInChild(int32_t aState)
  168. {
  169. // This should only be called in the content process, from ContentChild.cpp
  170. // in order to mirror the captive portal state set in the chrome process.
  171. MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default);
  172. mState = aState;
  173. mLastChecked = TimeStamp::Now();
  174. }
  175. //-----------------------------------------------------------------------------
  176. // CaptivePortalService::nsICaptivePortalService
  177. //-----------------------------------------------------------------------------
  178. NS_IMETHODIMP
  179. CaptivePortalService::GetState(int32_t *aState)
  180. {
  181. *aState = mState;
  182. return NS_OK;
  183. }
  184. NS_IMETHODIMP
  185. CaptivePortalService::RecheckCaptivePortal()
  186. {
  187. LOG(("CaptivePortalService::RecheckCaptivePortal\n"));
  188. if (XRE_GetProcessType() != GeckoProcessType_Default) {
  189. // Doesn't do anything if called in the content process.
  190. return NS_OK;
  191. }
  192. // This is called for user activity. We need to reset the slack count,
  193. // so the checks continue to be quite frequent.
  194. mSlackCount = 0;
  195. mDelay = mMinInterval;
  196. PerformCheck();
  197. RearmTimer();
  198. return NS_OK;
  199. }
  200. NS_IMETHODIMP
  201. CaptivePortalService::GetLastChecked(uint64_t *aLastChecked)
  202. {
  203. double duration = (TimeStamp::Now() - mLastChecked).ToMilliseconds();
  204. *aLastChecked = static_cast<uint64_t>(duration);
  205. return NS_OK;
  206. }
  207. //-----------------------------------------------------------------------------
  208. // CaptivePortalService::nsITimer
  209. // This callback gets called every mDelay miliseconds
  210. // It issues a checkCaptivePortal operation if one isn't already in progress
  211. //-----------------------------------------------------------------------------
  212. NS_IMETHODIMP
  213. CaptivePortalService::Notify(nsITimer *aTimer)
  214. {
  215. LOG(("CaptivePortalService::Notify\n"));
  216. MOZ_ASSERT(aTimer == mTimer);
  217. MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
  218. PerformCheck();
  219. // This is needed because we don't want to always make requests very often.
  220. // Every 10 checks, we the delay is increased mBackoffFactor times
  221. // to a maximum delay of mMaxInterval
  222. mSlackCount++;
  223. if (mSlackCount % 10 == 0) {
  224. mDelay = mDelay * mBackoffFactor;
  225. }
  226. if (mDelay > mMaxInterval) {
  227. mDelay = mMaxInterval;
  228. }
  229. // Note - if mDelay is 0, the timer will not be rearmed.
  230. RearmTimer();
  231. return NS_OK;
  232. }
  233. //-----------------------------------------------------------------------------
  234. // CaptivePortalService::nsIObserver
  235. //-----------------------------------------------------------------------------
  236. NS_IMETHODIMP
  237. CaptivePortalService::Observe(nsISupports *aSubject,
  238. const char * aTopic,
  239. const char16_t * aData)
  240. {
  241. if (XRE_GetProcessType() != GeckoProcessType_Default) {
  242. // Doesn't do anything if called in the content process.
  243. return NS_OK;
  244. }
  245. LOG(("CaptivePortalService::Observe() topic=%s\n", aTopic));
  246. if (!strcmp(aTopic, kOpenCaptivePortalLoginEvent)) {
  247. // A redirect or altered content has been detected.
  248. // The user needs to log in. We are in a captive portal.
  249. mState = LOCKED_PORTAL;
  250. mLastChecked = TimeStamp::Now();
  251. mEverBeenCaptive = true;
  252. } else if (!strcmp(aTopic, kCaptivePortalLoginSuccessEvent)) {
  253. // The user has successfully logged in. We have connectivity.
  254. mState = UNLOCKED_PORTAL;
  255. mLastChecked = TimeStamp::Now();
  256. mSlackCount = 0;
  257. mDelay = mMinInterval;
  258. RearmTimer();
  259. } else if (!strcmp(aTopic, kAbortCaptivePortalLoginEvent)) {
  260. // The login has been aborted
  261. mState = UNKNOWN;
  262. mLastChecked = TimeStamp::Now();
  263. mSlackCount = 0;
  264. }
  265. // Send notification so that the captive portal state is mirrored in the
  266. // content process.
  267. nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
  268. if (observerService) {
  269. nsCOMPtr<nsICaptivePortalService> cps(this);
  270. observerService->NotifyObservers(cps, NS_IPC_CAPTIVE_PORTAL_SET_STATE, nullptr);
  271. }
  272. return NS_OK;
  273. }
  274. //-----------------------------------------------------------------------------
  275. // CaptivePortalService::nsICaptivePortalCallback
  276. //-----------------------------------------------------------------------------
  277. NS_IMETHODIMP
  278. CaptivePortalService::Prepare()
  279. {
  280. LOG(("CaptivePortalService::Prepare\n"));
  281. MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
  282. // XXX: Finish preparation shouldn't be called until dns and routing is available.
  283. if (mCaptivePortalDetector) {
  284. mCaptivePortalDetector->FinishPreparation(kInterfaceName);
  285. }
  286. return NS_OK;
  287. }
  288. NS_IMETHODIMP
  289. CaptivePortalService::Complete(bool success)
  290. {
  291. LOG(("CaptivePortalService::Complete(success=%d) mState=%d\n", success, mState));
  292. MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
  293. mLastChecked = TimeStamp::Now();
  294. // Note: this callback gets called when:
  295. // 1. the request is completed, and content is valid (success == true)
  296. // 2. when the request is aborted or times out (success == false)
  297. if (success) {
  298. if (mEverBeenCaptive) {
  299. mState = UNLOCKED_PORTAL;
  300. } else {
  301. mState = NOT_CAPTIVE;
  302. }
  303. }
  304. mRequestInProgress = false;
  305. return NS_OK;
  306. }
  307. } // namespace net
  308. } // namespace mozilla