~ubuntu-branches/ubuntu/precise/kompozer/precise

« back to all changes in this revision

Viewing changes to mozilla/extensions/cookie/nsCookiePermission.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Yarusso
  • Date: 2007-08-27 01:11:03 UTC
  • Revision ID: james.westby@ubuntu.com-20070827011103-2jgf4s6532gqu2ka
Tags: upstream-0.7.10
ImportĀ upstreamĀ versionĀ 0.7.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// vim:ts=2:sw=2:et:
 
2
/* ***** BEGIN LICENSE BLOCK *****
 
3
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 
4
 *
 
5
 * The contents of this file are subject to the Mozilla Public License Version
 
6
 * 1.1 (the "License"); you may not use this file except in compliance with
 
7
 * the License. You may obtain a copy of the License at
 
8
 * http://www.mozilla.org/MPL/
 
9
 *
 
10
 * Software distributed under the License is distributed on an "AS IS" basis,
 
11
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 
12
 * for the specific language governing rights and limitations under the
 
13
 * License.
 
14
 *
 
15
 * The Original Code is cookie manager code.
 
16
 *
 
17
 * The Initial Developer of the Original Code is
 
18
 * Michiel van Leeuwen (mvl@exedo.nl).
 
19
 * Portions created by the Initial Developer are Copyright (C) 2003
 
20
 * the Initial Developer. All Rights Reserved.
 
21
 *
 
22
 * Contributor(s):
 
23
 *   Darin Fisher <darin@meer.net>
 
24
 *
 
25
 * Alternatively, the contents of this file may be used under the terms of
 
26
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 
27
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 
28
 * in which case the provisions of the GPL or the LGPL are applicable instead
 
29
 * of those above. If you wish to allow use of your version of this file only
 
30
 * under the terms of either the GPL or the LGPL, and not to allow others to
 
31
 * use your version of this file under the terms of the MPL, indicate your
 
32
 * decision by deleting the provisions above and replace them with the notice
 
33
 * and other provisions required by the GPL or the LGPL. If you do not delete
 
34
 * the provisions above, a recipient may use your version of this file under
 
35
 * the terms of any one of the MPL, the GPL or the LGPL.
 
36
 *
 
37
 * ***** END LICENSE BLOCK ***** */
 
38
 
 
39
 
 
40
#include "nsCookiePermission.h"
 
41
#include "nsICookie2.h"
 
42
#include "nsIServiceManager.h"
 
43
#include "nsICookiePromptService.h"
 
44
#include "nsICookieManager2.h"
 
45
#include "nsNetCID.h"
 
46
#include "nsIURI.h"
 
47
#include "nsIPrefService.h"
 
48
#include "nsIPrefBranch.h"
 
49
#include "nsIPrefBranchInternal.h"
 
50
#include "nsIDocShell.h"
 
51
#include "nsIDocShellTreeItem.h"
 
52
#include "nsIInterfaceRequestor.h"
 
53
#include "nsIInterfaceRequestorUtils.h"
 
54
#include "nsILoadGroup.h"
 
55
#include "nsIChannel.h"
 
56
#include "nsIDOMWindow.h"
 
57
#include "nsString.h"
 
58
#include "nsCRT.h"
 
59
 
 
60
/****************************************************************
 
61
 ************************ nsCookiePermission ********************
 
62
 ****************************************************************/
 
63
 
 
64
// values for mCookiesLifetimePolicy
 
65
// 0 == accept normally
 
66
// 1 == ask before accepting
 
67
// 2 == downgrade to session
 
68
// 3 == limit lifetime to N days
 
69
static const PRUint32 ACCEPT_NORMALLY = 0;
 
70
static const PRUint32 ASK_BEFORE_ACCEPT = 1;
 
71
static const PRUint32 ACCEPT_SESSION = 2;
 
72
static const PRUint32 ACCEPT_FOR_N_DAYS = 3;
 
73
 
 
74
static const PRBool kDefaultPolicy = PR_TRUE;
 
75
static const char kCookiesLifetimePolicy[] = "network.cookie.lifetimePolicy";
 
76
static const char kCookiesLifetimeDays[] = "network.cookie.lifetime.days";
 
77
static const char kCookiesAlwaysAcceptSession[] = "network.cookie.alwaysAcceptSessionCookies";
 
78
#ifdef MOZ_MAIL_NEWS
 
79
static const char kCookiesDisabledForMailNews[] = "network.cookie.disableCookieForMailNews";
 
80
#endif
 
81
 
 
82
static const char kCookiesPrefsMigrated[] = "network.cookie.prefsMigrated";
 
83
// obsolete pref names for migration
 
84
static const char kCookiesLifetimeEnabled[] = "network.cookie.lifetime.enabled";
 
85
static const char kCookiesLifetimeBehavior[] = "network.cookie.lifetime.behavior";
 
86
static const char kCookiesAskPermission[] = "network.cookie.warnAboutCookies";
 
87
 
 
88
static const char kPermissionType[] = "cookie";
 
89
 
 
90
// XXX these casts and constructs are horrible, but our nsInt64/nsTime
 
91
// classes are lacking so we need them for now. see bug 198694.
 
92
#define USEC_PER_SEC   (nsInt64(1000000))
 
93
#define NOW_IN_SECONDS (nsInt64(PR_Now()) / USEC_PER_SEC)
 
94
 
 
95
#ifdef MOZ_MAIL_NEWS
 
96
// returns PR_TRUE if URI appears to be the URI of a mailnews protocol
 
97
static PRBool
 
98
IsFromMailNews(nsIURI *aURI)
 
99
{
 
100
  static const char *kMailNewsProtocols[] =
 
101
      { "imap", "news", "snews", "mailbox", nsnull };
 
102
  PRBool result;
 
103
  for (const char **p = kMailNewsProtocols; *p; ++p) {
 
104
    if (NS_SUCCEEDED(aURI->SchemeIs(*p, &result)) && result)
 
105
      return PR_TRUE;
 
106
  }
 
107
  return PR_FALSE;
 
108
}
 
109
#endif
 
110
 
 
111
static void
 
112
GetInterfaceFromChannel(nsIChannel   *aChannel,
 
113
                        const nsIID  &aIID,
 
114
                        void        **aResult)
 
115
{
 
116
  if (!aChannel)
 
117
    return; // no context, no interface!
 
118
  NS_ASSERTION(!*aResult, "result not initialized to null");
 
119
 
 
120
  nsCOMPtr<nsIInterfaceRequestor> cbs;
 
121
  aChannel->GetNotificationCallbacks(getter_AddRefs(cbs));
 
122
  if (cbs)
 
123
    cbs->GetInterface(aIID, aResult);
 
124
  if (!*aResult) {
 
125
    // try load group's notification callbacks...
 
126
    nsCOMPtr<nsILoadGroup> loadGroup;
 
127
    aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
 
128
    if (loadGroup) {
 
129
      loadGroup->GetNotificationCallbacks(getter_AddRefs(cbs));
 
130
      if (cbs)
 
131
        cbs->GetInterface(aIID, aResult);
 
132
    }
 
133
  }
 
134
}
 
135
 
 
136
NS_IMPL_ISUPPORTS2(nsCookiePermission,
 
137
                   nsICookiePermission,
 
138
                   nsIObserver)
 
139
 
 
140
nsresult
 
141
nsCookiePermission::Init()
 
142
{
 
143
  nsresult rv;
 
144
  mPermMgr = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
 
145
  if (NS_FAILED(rv)) return rv;
 
146
 
 
147
  // failure to access the pref service is non-fatal...
 
148
  nsCOMPtr<nsIPrefBranchInternal> prefBranch =
 
149
      do_GetService(NS_PREFSERVICE_CONTRACTID);
 
150
  if (prefBranch) {
 
151
    prefBranch->AddObserver(kCookiesLifetimePolicy, this, PR_FALSE);
 
152
    prefBranch->AddObserver(kCookiesLifetimeDays, this, PR_FALSE);
 
153
    prefBranch->AddObserver(kCookiesAlwaysAcceptSession, this, PR_FALSE);
 
154
#ifdef MOZ_MAIL_NEWS
 
155
    prefBranch->AddObserver(kCookiesDisabledForMailNews, this, PR_FALSE);
 
156
#endif
 
157
    PrefChanged(prefBranch, nsnull);
 
158
 
 
159
    // migration code for original cookie prefs
 
160
    PRBool migrated;
 
161
    rv = prefBranch->GetBoolPref(kCookiesPrefsMigrated, &migrated);
 
162
    if (NS_FAILED(rv) || !migrated) {
 
163
      PRBool warnAboutCookies = PR_FALSE;
 
164
      prefBranch->GetBoolPref(kCookiesAskPermission, &warnAboutCookies);
 
165
 
 
166
      // if the user is using ask before accepting, we'll use that
 
167
      if (warnAboutCookies)
 
168
        prefBranch->SetIntPref(kCookiesLifetimePolicy, ASK_BEFORE_ACCEPT);
 
169
        
 
170
      PRBool lifetimeEnabled = PR_FALSE;
 
171
      prefBranch->GetBoolPref(kCookiesLifetimeEnabled, &lifetimeEnabled);
 
172
      
 
173
      // if they're limiting lifetime and not using the prompts, use the 
 
174
      // appropriate limited lifetime pref
 
175
      if (lifetimeEnabled && !warnAboutCookies) {
 
176
        PRInt32 lifetimeBehavior;
 
177
        prefBranch->GetIntPref(kCookiesLifetimeBehavior, &lifetimeBehavior);
 
178
        if (lifetimeBehavior)
 
179
          prefBranch->SetIntPref(kCookiesLifetimePolicy, ACCEPT_FOR_N_DAYS);
 
180
        else
 
181
          prefBranch->SetIntPref(kCookiesLifetimePolicy, ACCEPT_SESSION);
 
182
      }
 
183
      prefBranch->SetBoolPref(kCookiesPrefsMigrated, PR_TRUE);
 
184
    }
 
185
  }
 
186
 
 
187
  return NS_OK;
 
188
}
 
189
 
 
190
void
 
191
nsCookiePermission::PrefChanged(nsIPrefBranch *aPrefBranch,
 
192
                                const char    *aPref)
 
193
{
 
194
  PRBool val;
 
195
 
 
196
#define PREF_CHANGED(_P) (!aPref || !strcmp(aPref, _P))
 
197
 
 
198
  if (PREF_CHANGED(kCookiesLifetimePolicy) &&
 
199
      NS_SUCCEEDED(aPrefBranch->GetIntPref(kCookiesLifetimePolicy, &val)))
 
200
    mCookiesLifetimePolicy = val;
 
201
 
 
202
  if (PREF_CHANGED(kCookiesLifetimeDays) &&
 
203
      NS_SUCCEEDED(aPrefBranch->GetIntPref(kCookiesLifetimeDays, &val)))
 
204
    // save cookie lifetime in seconds instead of days
 
205
    mCookiesLifetimeSec = val * 24 * 60 * 60;
 
206
 
 
207
  if (PREF_CHANGED(kCookiesAlwaysAcceptSession) &&
 
208
      NS_SUCCEEDED(aPrefBranch->GetBoolPref(kCookiesAlwaysAcceptSession, &val)))
 
209
    mCookiesAlwaysAcceptSession = val;
 
210
 
 
211
#ifdef MOZ_MAIL_NEWS
 
212
  if (PREF_CHANGED(kCookiesDisabledForMailNews) &&
 
213
      NS_SUCCEEDED(aPrefBranch->GetBoolPref(kCookiesDisabledForMailNews, &val)))
 
214
    mCookiesDisabledForMailNews = val;
 
215
#endif
 
216
}
 
217
 
 
218
NS_IMETHODIMP
 
219
nsCookiePermission::SetAccess(nsIURI         *aURI,
 
220
                              nsCookieAccess  aAccess)
 
221
{
 
222
  //
 
223
  // NOTE: nsCookieAccess values conveniently match up with
 
224
  //       the permission codes used by nsIPermissionManager.
 
225
  //       this is nice because it avoids conversion code.
 
226
  //
 
227
  return mPermMgr->Add(aURI, kPermissionType, aAccess);
 
228
}
 
229
 
 
230
NS_IMETHODIMP
 
231
nsCookiePermission::CanAccess(nsIURI         *aURI,
 
232
                              nsIURI         *aFirstURI,
 
233
                              nsIChannel     *aChannel,
 
234
                              nsCookieAccess *aResult)
 
235
{
 
236
#ifdef MOZ_MAIL_NEWS
 
237
  // disable cookies in mailnews if user's prefs say so
 
238
  if (mCookiesDisabledForMailNews) {
 
239
    //
 
240
    // try to examine the "app type" of the docshell owning this request.  if
 
241
    // we find a docshell in the heirarchy of type APP_TYPE_MAIL, then assume
 
242
    // this URI is being loaded from within mailnews.
 
243
    //
 
244
    // XXX this is a pretty ugly hack at the moment since cookies really
 
245
    // shouldn't have to talk to the docshell directly.  ultimately, we want
 
246
    // to talk to some more generic interface, which the docshell would also
 
247
    // implement.  but, the basic mechanism here of leveraging the channel's
 
248
    // (or loadgroup's) notification callbacks attribute seems ideal as it
 
249
    // avoids the problem of having to modify all places in the code which
 
250
    // kick off network requests.
 
251
    //
 
252
    PRUint32 appType = nsIDocShell::APP_TYPE_UNKNOWN;
 
253
    if (aChannel) {
 
254
      nsCOMPtr<nsIDocShellTreeItem> item, parent;
 
255
      GetInterfaceFromChannel(aChannel, NS_GET_IID(nsIDocShellTreeItem),
 
256
                              getter_AddRefs(parent));
 
257
      if (parent) {
 
258
        do {
 
259
            item = parent;
 
260
            nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(item);
 
261
            if (docshell)
 
262
              docshell->GetAppType(&appType);
 
263
        } while (appType != nsIDocShell::APP_TYPE_MAIL &&
 
264
                 NS_SUCCEEDED(item->GetParent(getter_AddRefs(parent))) &&
 
265
                 parent);
 
266
      }
 
267
    }
 
268
    if ((appType == nsIDocShell::APP_TYPE_MAIL) ||
 
269
        (aFirstURI && IsFromMailNews(aFirstURI)) ||
 
270
        IsFromMailNews(aURI)) {
 
271
      *aResult = ACCESS_DENY;
 
272
      return NS_OK;
 
273
    }
 
274
  }
 
275
#endif // MOZ_MAIL_NEWS
 
276
  
 
277
  // finally, check with permission manager...
 
278
  nsresult rv = mPermMgr->TestPermission(aURI, kPermissionType, (PRUint32 *) aResult);
 
279
  if (NS_SUCCEEDED(rv)) {
 
280
    switch (*aResult) {
 
281
    // if we have one of the publicly-available values, just return it
 
282
    case nsIPermissionManager::UNKNOWN_ACTION: // ACCESS_DEFAULT
 
283
    case nsIPermissionManager::ALLOW_ACTION:   // ACCESS_ALLOW
 
284
    case nsIPermissionManager::DENY_ACTION:    // ACCESS_DENY
 
285
      break;
 
286
 
 
287
    // ACCESS_SESSION means the cookie can be accepted; the session 
 
288
    // downgrade will occur in CanSetCookie().
 
289
    case nsICookiePermission::ACCESS_SESSION:
 
290
      *aResult = ACCESS_ALLOW;
 
291
      break;
 
292
 
 
293
    // ack, an unknown type! just use the defaults.
 
294
    default:
 
295
      *aResult = ACCESS_DEFAULT;
 
296
    }
 
297
  }
 
298
 
 
299
  return rv;
 
300
}
 
301
 
 
302
NS_IMETHODIMP 
 
303
nsCookiePermission::CanSetCookie(nsIURI     *aURI,
 
304
                                 nsIChannel *aChannel,
 
305
                                 nsICookie2 *aCookie,
 
306
                                 PRBool     *aIsSession,
 
307
                                 PRInt64    *aExpiry,
 
308
                                 PRBool     *aResult)
 
309
{
 
310
  NS_ASSERTION(aURI, "null uri");
 
311
 
 
312
  *aResult = kDefaultPolicy;
 
313
 
 
314
  PRUint32 perm;
 
315
  mPermMgr->TestPermission(aURI, kPermissionType, &perm);
 
316
  switch (perm) {
 
317
  case nsICookiePermission::ACCESS_SESSION:
 
318
    *aIsSession = PR_TRUE;
 
319
 
 
320
  case nsIPermissionManager::ALLOW_ACTION: // ACCESS_ALLOW
 
321
    *aResult = PR_TRUE;
 
322
    break;
 
323
 
 
324
  case nsIPermissionManager::DENY_ACTION:  // ACCESS_DENY
 
325
    *aResult = PR_FALSE;
 
326
    break;
 
327
 
 
328
  default:
 
329
    // the permission manager has nothing to say about this cookie -
 
330
    // so, we apply the default prefs to it.
 
331
    NS_ASSERTION(perm == nsIPermissionManager::UNKNOWN_ACTION, "unknown permission");
 
332
    
 
333
    // now we need to figure out what type of accept policy we're dealing with
 
334
    // if we accept cookies normally, just bail and return
 
335
    if (mCookiesLifetimePolicy == ACCEPT_NORMALLY) {
 
336
      *aResult = PR_TRUE;
 
337
      return NS_OK;
 
338
    }
 
339
    
 
340
    // declare this here since it'll be used in all of the remaining cases
 
341
    nsInt64 currentTime = NOW_IN_SECONDS;
 
342
    nsInt64 delta = nsInt64(*aExpiry) - currentTime;
 
343
    
 
344
    // check whether the user wants to be prompted
 
345
    if (mCookiesLifetimePolicy == ASK_BEFORE_ACCEPT) {
 
346
      // if it's a session cookie and the user wants to accept these 
 
347
      // without asking, just accept the cookie and return
 
348
      if (*aIsSession && mCookiesAlwaysAcceptSession) {
 
349
        *aResult = PR_TRUE;
 
350
        return NS_OK;
 
351
      }
 
352
      
 
353
      // default to rejecting, in case the prompting process fails
 
354
      *aResult = PR_FALSE;
 
355
 
 
356
      nsCAutoString hostPort;
 
357
      aURI->GetHostPort(hostPort);
 
358
 
 
359
      if (!aCookie) {
 
360
         return NS_ERROR_UNEXPECTED;
 
361
      }
 
362
      // If there is no host, use the scheme, and append "://",
 
363
      // to make sure it isn't a host or something.
 
364
      // This is done to make the dialog appear for javascript cookies from
 
365
      // file:// urls, and make the text on it not too weird. (bug 209689)
 
366
      if (hostPort.IsEmpty()) {
 
367
        aURI->GetScheme(hostPort);
 
368
        if (hostPort.IsEmpty()) {
 
369
          // still empty. Just return the default.
 
370
          return NS_OK;
 
371
        }
 
372
        hostPort = hostPort + NS_LITERAL_CSTRING("://");
 
373
      }
 
374
 
 
375
      // we don't cache the cookiePromptService - it's not used often, so not
 
376
      // worth the memory.
 
377
      nsresult rv;
 
378
      nsCOMPtr<nsICookiePromptService> cookiePromptService =
 
379
          do_GetService(NS_COOKIEPROMPTSERVICE_CONTRACTID, &rv);
 
380
      if (NS_FAILED(rv)) return rv;
 
381
 
 
382
      // try to get a nsIDOMWindow from the channel...
 
383
      nsCOMPtr<nsIDOMWindow> parent;
 
384
      GetInterfaceFromChannel(aChannel, NS_GET_IID(nsIDOMWindow),
 
385
                              getter_AddRefs(parent));
 
386
 
 
387
      // get some useful information to present to the user:
 
388
      // whether a previous cookie already exists, and how many cookies this host
 
389
      // has set
 
390
      PRBool foundCookie;
 
391
      PRUint32 countFromHost;
 
392
      nsCOMPtr<nsICookieManager2> cookieManager = do_GetService(NS_COOKIEMANAGER_CONTRACTID, &rv);
 
393
      if (NS_SUCCEEDED(rv))
 
394
        rv = cookieManager->FindMatchingCookie(aCookie, &countFromHost, &foundCookie);
 
395
      if (NS_FAILED(rv)) return rv;
 
396
 
 
397
      // check if the cookie we're trying to set is already expired, and return;
 
398
      // but only if there's no previous cookie, because then we need to delete the previous
 
399
      // cookie. we need this check to avoid prompting the user for already-expired cookies.
 
400
      if (!foundCookie && !*aIsSession && delta <= nsInt64(0)) {
 
401
        // the cookie has already expired. accept it, and let the backend figure
 
402
        // out it's expired, so that we get correct logging & notifications.
 
403
        *aResult = PR_TRUE;
 
404
        return rv;
 
405
      }
 
406
 
 
407
      PRBool rememberDecision = PR_FALSE;
 
408
      rv = cookiePromptService->CookieDialog(parent, aCookie, hostPort, 
 
409
                                             countFromHost, foundCookie,
 
410
                                             &rememberDecision, aResult);
 
411
      if (NS_FAILED(rv)) return rv;
 
412
      
 
413
      if (*aResult == nsICookiePromptService::ACCEPT_SESSION_COOKIE)
 
414
        *aIsSession = PR_TRUE;
 
415
 
 
416
      if (rememberDecision) {
 
417
        switch (*aResult) {
 
418
          case nsICookiePromptService::DENY_COOKIE:
 
419
            mPermMgr->Add(aURI, kPermissionType, (PRUint32) nsIPermissionManager::DENY_ACTION);
 
420
            break;
 
421
          case nsICookiePromptService::ACCEPT_COOKIE:
 
422
            mPermMgr->Add(aURI, kPermissionType, (PRUint32) nsIPermissionManager::ALLOW_ACTION);
 
423
            break;
 
424
          case nsICookiePromptService::ACCEPT_SESSION_COOKIE:
 
425
            mPermMgr->Add(aURI, kPermissionType, nsICookiePermission::ACCESS_SESSION);
 
426
            break;
 
427
          default:
 
428
            break;
 
429
        }
 
430
      }
 
431
    } else {
 
432
      // we're not prompting, so we must be limiting the lifetime somehow
 
433
      // if it's a session cookie, we do nothing
 
434
      if (!*aIsSession && delta > nsInt64(0)) {
 
435
        if (mCookiesLifetimePolicy == ACCEPT_SESSION) {
 
436
          // limit lifetime to session
 
437
          *aIsSession = PR_TRUE;
 
438
        } else if (delta > mCookiesLifetimeSec) {
 
439
          // limit lifetime to specified time
 
440
          *aExpiry = currentTime + mCookiesLifetimeSec;
 
441
        }
 
442
      }
 
443
    }
 
444
  }
 
445
 
 
446
  return NS_OK;
 
447
}
 
448
 
 
449
NS_IMETHODIMP
 
450
nsCookiePermission::Observe(nsISupports     *aSubject,
 
451
                            const char      *aTopic,
 
452
                            const PRUnichar *aData)
 
453
{
 
454
  nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(aSubject);
 
455
  NS_ASSERTION(!nsCRT::strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic),
 
456
               "unexpected topic - we only deal with pref changes!");
 
457
 
 
458
  if (prefBranch)
 
459
    PrefChanged(prefBranch, NS_LossyConvertUTF16toASCII(aData).get());
 
460
  return NS_OK;
 
461
}