Issue #1258 - Part 1: Import mailnews, ldap, and mork from comm-esr52.9.1

This commit is contained in:
Matt A. Tobin 2019-11-03 00:17:46 -04:00 • committed by Roy Tam
commit e400f4130a
1564 changed files with 510348 additions and 0 deletions

View file

@ -0,0 +1,6 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DIRS += ['src']

View file

@ -0,0 +1,11 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
SOURCES += [
'nsBayesianFilter.cpp',
]
FINAL_LIBRARY = 'mail'

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,404 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsBayesianFilter_h__
#define nsBayesianFilter_h__
#include <stdio.h>
#include "nsCOMPtr.h"
#include "nsIMsgFilterPlugin.h"
#include "nsISemanticUnitScanner.h"
#include "PLDHashTable.h"
#include "nsITimer.h"
#include "nsTArray.h"
#include "nsStringGlue.h"
#include "nsWeakReference.h"
#include "nsIObserver.h"
// XXX can't simply byte align arenas, must at least 2-byte align.
#define PL_ARENA_CONST_ALIGN_MASK 1
#include "plarena.h"
#define DEFAULT_MIN_INTERVAL_BETWEEN_WRITES 15*60*1000
struct Token;
class TokenEnumeration;
class TokenAnalyzer;
class nsIMsgWindow;
class nsIMimeHeaders;
class nsIUTF8StringEnumerator;
struct BaseToken;
struct CorpusToken;
/**
* Helper class to enumerate Token objects in a PLDHashTable
* safely and without copying (see bugzilla #174859). The
* enumeration is safe to use until an Add()
* or Remove() is performed on the table.
*/
class TokenEnumeration {
public:
TokenEnumeration(PLDHashTable* table);
bool hasMoreTokens();
BaseToken* nextToken();
private:
PLDHashTable::Iterator mIterator;
};
// A trait is some aspect of a message, like being junk or tagged as
// Personal, that the statistical classifier should track. The Trait
// structure is a per-token representation of information pertaining to
// a message trait.
//
// Traits per token are maintained as a linked list.
//
struct TraitPerToken
{
uint32_t mId; // identifying number for a trait
uint32_t mCount; // count of messages with this token and trait
uint32_t mNextLink; // index in mTraitStore for the next trait, or 0
// for none
TraitPerToken(uint32_t aId, uint32_t aCount); // inititializer
};
// An Analysis is the statistical results for a particular message, a
// particular token, and for a particular pair of trait/antitrait, that
// is then used in subsequent analysis to score the message.
//
// Analyses per token are maintained as a linked list.
//
struct AnalysisPerToken
{
uint32_t mTraitIndex; // index representing a protrait/antitrait pair.
// So if we are analyzing 3 different traits, then
// the first trait is 0, the second 1, etc.
double mDistance; // absolute value of mProbability - 0.5
double mProbability; // relative indicator of match of trait to token
uint32_t mNextLink; // index in mAnalysisStore for the Analysis object
// for the next trait index, or 0 for none.
// initializer
AnalysisPerToken(uint32_t aTraitIndex, double aDistance, double aProbability);
};
class TokenHash {
public:
virtual ~TokenHash();
/**
* Clears out the previous message tokens.
*/
nsresult clearTokens();
uint32_t countTokens();
TokenEnumeration getTokens();
BaseToken* add(const char* word);
protected:
TokenHash(uint32_t entrySize);
PLArenaPool mWordPool;
uint32_t mEntrySize;
PLDHashTable mTokenTable;
char* copyWord(const char* word, uint32_t len);
BaseToken* get(const char* word);
};
class Tokenizer: public TokenHash {
public:
Tokenizer();
~Tokenizer();
Token* get(const char* word);
// The training set keeps an occurrence count on each word. This count
// is supposed to count the # of messsages it occurs in.
// When add/remove is called while tokenizing a message and NOT the training set,
//
Token* add(const char* word, uint32_t count = 1);
Token* copyTokens();
void tokenize(const char* text);
/**
* Creates specific tokens based on the mime headers for the message being tokenized
*/
void tokenizeHeaders(nsIUTF8StringEnumerator * aHeaderNames, nsIUTF8StringEnumerator * aHeaderValues);
void tokenizeAttachment(const char * aContentType, const char * aFileName);
nsCString mBodyDelimiters; // delimiters for body tokenization
nsCString mHeaderDelimiters; // delimiters for header tokenization
// arrays of extra headers to tokenize / to not tokenize
nsTArray<nsCString> mEnabledHeaders;
nsTArray<nsCString> mDisabledHeaders;
// Delimiters used in tokenizing a particular header.
// Parallel array to mEnabledHeaders
nsTArray<nsCString> mEnabledHeadersDelimiters;
bool mCustomHeaderTokenization; // Are there any preference-set tokenization customizations?
uint32_t mMaxLengthForToken; // maximum length of a token
// should we convert iframe to div during tokenization?
bool mIframeToDiv;
private:
void tokenize_ascii_word(char * word);
void tokenize_japanese_word(char* chunk);
inline void addTokenForHeader(const char * aTokenPrefix, nsACString& aValue,
bool aTokenizeValue = false, const char* aDelimiters = nullptr);
nsresult stripHTML(const nsAString& inString, nsAString& outString);
// helper function to escape \n, \t, etc from a CString
void UnescapeCString(nsCString& aCString);
private:
nsCOMPtr<nsISemanticUnitScanner> mScanner;
};
/**
* Implements storage of a collection of message tokens and counts for
* a corpus of classified messages
*/
class CorpusStore: public TokenHash {
public:
CorpusStore();
~CorpusStore();
/**
* retrieve the token structure for a particular string
*
* @param word the character representation of the token
*
* @return token structure containing counts, null if not found
*/
CorpusToken* get(const char* word);
/**
* add tokens to the storage, or increment counts if already exists.
*
* @param aTokenizer tokenizer for the list of tokens to remember
* @param aTraitId id for the trait whose counts will be remembered
* @param aCount number of new messages represented by the token list
*/
void rememberTokens(Tokenizer& aTokenizer, uint32_t aTraitId, uint32_t aCount);
/**
* decrement counts for tokens in the storage, removing if all counts
* are zero
*
* @param aTokenizer tokenizer for the list of tokens to forget
* @param aTraitId id for the trait whose counts will be removed
* @param aCount number of messages represented by the token list
*/
void forgetTokens(Tokenizer& aTokenizer, uint32_t aTraitId, uint32_t aCount);
/**
* write the corpus information to file storage
*
* @param aMaximumTokenCount prune tokens if number of tokens exceeds
* this value. == 0 for no pruning
*/
void writeTrainingData(uint32_t aMaximumTokenCount);
/**
* read the corpus information from file storage
*/
void readTrainingData();
/**
* delete the local corpus storage file and data
*/
nsresult resetTrainingData();
/**
* get the count of messages whose tokens are stored that are associated
* with a trait
*
* @param aTraitId identifier for the trait
* @return number of messages for that trait
*/
uint32_t getMessageCount(uint32_t aTraitId);
/**
* set the count of messages whose tokens are stored that are associated
* with a trait
*
* @param aTraitId identifier for the trait
* @param aCount number of messages for that trait
*/
void setMessageCount(uint32_t aTraitId, uint32_t aCount);
/**
* get the count of messages associated with a particular token and trait
*
* @param token the token string and associated counts
* @param aTraitId identifier for the trait
*/
uint32_t getTraitCount(CorpusToken *token, uint32_t aTraitId);
/**
* Add (or remove) data from a particular file to the corpus data.
*
* @param aFile the file with the data, in the format:
*
* Format of the trait file for version 1:
* [0xFCA93601] (the 01 is the version)
* for each trait to write:
* [id of trait to write] (0 means end of list)
* [number of messages per trait]
* for each token with non-zero count
* [count]
* [length of word]word
*
* @param aIsAdd should the data be added, or removed? true if adding,
* else removing.
*
* @param aRemapCount number of items in the parallel arrays aFromTraits,
* aToTraits. These arrays allow conversion of the
* trait id stored in the file (which may be originated
* externally) to the trait id used in the local corpus
* (which is defined locally using nsIMsgTraitService).
*
* @param aFromTraits array of trait ids used in aFile. If aFile contains
* trait ids that are not in this array, they are not
* remapped, but assummed to be local trait ids.
*
* @param aToTraits array of trait ids, corresponding to elements of
* aFromTraits, that represent the local trait ids to be
* used in storing data from aFile into the local corpus.
*
*/
nsresult UpdateData(nsIFile *aFile, bool aIsAdd,
uint32_t aRemapCount, uint32_t *aFromTraits,
uint32_t *aToTraits);
/**
* remove all counts (message and tokens) for a trait id
*
* @param aTrait trait id for the trait to remove
*/
nsresult ClearTrait(uint32_t aTrait);
protected:
/**
* return the local corpus storage file for junk traits
*/
nsresult getTrainingFile(nsIFile ** aFile);
/**
* return the local corpus storage file for non-junk traits
*/
nsresult getTraitFile(nsIFile ** aFile);
/**
* read token strings from the data file
*
* @param stream file stream with token data
* @param fileSize file size
* @param aTraitId id for the trait whose counts will be read
* @param aIsAdd true to add the counts, false to remove them
*
* @return true if successful, false if error
*/
bool readTokens(FILE* stream, int64_t fileSize, uint32_t aTraitId,
bool aIsAdd);
/**
* write token strings to the data file
*/
bool writeTokens(FILE* stream, bool shrink, uint32_t aTraitId);
/**
* remove counts for a token string
*/
void remove(const char* word, uint32_t aTraitId, uint32_t aCount);
/**
* add counts for a token string, adding the token string if new
*/
CorpusToken* add(const char* word, uint32_t aTraitId, uint32_t aCount);
/**
* change counts in a trait in the traits array, adding the trait if needed
*/
nsresult updateTrait(CorpusToken* token, uint32_t aTraitId,
int32_t aCountChange);
nsCOMPtr<nsIFile> mTrainingFile; // file used to store junk training data
nsCOMPtr<nsIFile> mTraitFile; // file used to store non-junk
// training data
nsTArray<TraitPerToken> mTraitStore; // memory for linked-list of counts
uint32_t mNextTraitIndex; // index in mTraitStore to first empty
// TraitPerToken
nsTArray<uint32_t> mMessageCounts; // count of messages per trait
// represented in the store
nsTArray<uint32_t> mMessageCountsId; // Parallel array to mMessageCounts, with
// the corresponding trait ID
};
class nsBayesianFilter : public nsIJunkMailPlugin, nsIMsgCorpus,
nsIObserver, nsSupportsWeakReference {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIMSGFILTERPLUGIN
NS_DECL_NSIJUNKMAILPLUGIN
NS_DECL_NSIMSGCORPUS
NS_DECL_NSIOBSERVER
nsBayesianFilter();
nsresult Init();
nsresult tokenizeMessage(const char* messageURI, nsIMsgWindow *aMsgWindow, TokenAnalyzer* analyzer);
void classifyMessage(Tokenizer& tokens, const char* messageURI,
nsIJunkMailClassificationListener* listener);
void classifyMessage(
Tokenizer& tokenizer,
const char* messageURI,
nsTArray<uint32_t>& aProTraits,
nsTArray<uint32_t>& aAntiTraits,
nsIJunkMailClassificationListener* listener,
nsIMsgTraitClassificationListener* aTraitListener,
nsIMsgTraitDetailListener* aDetailListener);
void observeMessage(Tokenizer& tokens, const char* messageURI,
nsTArray<uint32_t>& oldClassifications,
nsTArray<uint32_t>& newClassifications,
nsIJunkMailClassificationListener* listener,
nsIMsgTraitClassificationListener* aTraitListener);
protected:
virtual ~nsBayesianFilter();
static void TimerCallback(nsITimer* aTimer, void* aClosure);
CorpusStore mCorpus;
double mJunkProbabilityThreshold;
int32_t mMaximumTokenCount;
bool mTrainingDataDirty;
int32_t mMinFlushInterval; // in milliseconds, must be positive
//and not too close to 0
nsCOMPtr<nsITimer> mTimer;
// index in mAnalysisStore for first empty AnalysisPerToken
uint32_t mNextAnalysisIndex;
// memory for linked list of AnalysisPerToken objects
nsTArray<AnalysisPerToken> mAnalysisStore;
/**
* Determine the location in mAnalysisStore where the AnalysisPerToken
* object for a particular token and trait is stored
*/
uint32_t getAnalysisIndex(Token& token, uint32_t aTraitIndex);
/**
* Set the value of the AnalysisPerToken object for a particular
* token and trait
*/
nsresult setAnalysis(Token& token, uint32_t aTraitIndex,
double aDistance, double aProbability);
};
#endif // _nsBayesianFilter_h__

View file

@ -0,0 +1,22 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsBayesianFilterCID_h__
#define nsBayesianFilterCID_h__
#include "nsISupports.h"
#include "nsIFactory.h"
#include "nsIComponentManager.h"
#include "nsIMsgMdnGenerator.h"
#define NS_BAYESIANFILTER_CONTRACTID \
"@mozilla.org/messenger/filter-plugin;1?name=bayesianfilter"
#define NS_BAYESIANFILTER_CID \
{ /* F1070BFA-D539-11D6-90CA-00039310A47A */ \
0xF1070BFA, 0xD539, 0x11D6, \
{ 0x90, 0xCA, 0x00, 0x03, 0x93, 0x10, 0xA4, 0x7A }}
#endif /* nsBayesianFilterCID_h__ */

View file

@ -0,0 +1,259 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsIncompleteGamma_h__
#define nsIncompleteGamma_h__
/* An implementation of the incomplete gamma functions for real
arguments. P is defined as
x
/
1 [ a - 1 - t
P(a, x) = -------- I t e dt
Gamma(a) ]
/
0
and
infinity
/
1 [ a - 1 - t
Q(a, x) = -------- I t e dt
Gamma(a) ]
/
x
so that P(a,x) + Q(a,x) = 1.
Both a series expansion and a continued fraction exist. This
implementation uses the more efficient method based on the arguments.
Either case involves calculating a multiplicative term:
e^(-x)*x^a/Gamma(a).
Here we calculate the log of this term. Most math libraries have a
"lgamma" function but it is not re-entrant. Some libraries have a
"lgamma_r" which is re-entrant. Use it if possible. I have included a
simple replacement but it is certainly not as accurate.
Relative errors are almost always < 1e-10 and usually < 1e-14. Very
small and very large arguments cause trouble.
The region where a < 0.5 and x < 0.5 has poor error properties and is
not too stable. Get a better routine if you need results in this
region.
The error argument will be set negative if there is a domain error or
positive for an internal calculation error, currently lack of
convergence. A value is always returned, though.
*/
#include <math.h>
#include <float.h>
// the main routine
static double nsIncompleteGammaP (double a, double x, int *error);
// nsLnGamma(z): either a wrapper around lgamma_r or the internal function.
// C_m = B[2*m]/(2*m*(2*m-1)) where B is a Bernoulli number
static const double C_1 = 1.0 / 12.0;
static const double C_2 = -1.0 / 360.0;
static const double C_3 = 1.0 / 1260.0;
static const double C_4 = -1.0 / 1680.0;
static const double C_5 = 1.0 / 1188.0;
static const double C_6 = -691.0 / 360360.0;
static const double C_7 = 1.0 / 156.0;
static const double C_8 = -3617.0 / 122400.0;
static const double C_9 = 43867.0 / 244188.0;
static const double C_10 = -174611.0 / 125400.0;
static const double C_11 = 77683.0 / 5796.0;
// truncated asymptotic series in 1/z
static inline double lngamma_asymp (double z)
{
double w, w2, sum;
w = 1.0 / z;
w2 = w * w;
sum = w * (w2 * (w2 * (w2 * (w2 * (w2 * (w2 * (w2 * (w2 * (w2
* (C_11 * w2 + C_10) + C_9) + C_8) + C_7) + C_6)
+ C_5) + C_4) + C_3) + C_2) + C_1);
return sum;
}
struct fact_table_s
{
double fact;
double lnfact;
};
// for speed and accuracy
static const struct fact_table_s FactTable[] = {
{1.000000000000000, 0.0000000000000000000000e+00},
{1.000000000000000, 0.0000000000000000000000e+00},
{2.000000000000000, 6.9314718055994530942869e-01},
{6.000000000000000, 1.7917594692280550007892e+00},
{24.00000000000000, 3.1780538303479456197550e+00},
{120.0000000000000, 4.7874917427820459941458e+00},
{720.0000000000000, 6.5792512120101009952602e+00},
{5040.000000000000, 8.5251613610654142999881e+00},
{40320.00000000000, 1.0604602902745250228925e+01},
{362880.0000000000, 1.2801827480081469610995e+01},
{3628800.000000000, 1.5104412573075515295248e+01},
{39916800.00000000, 1.7502307845873885839769e+01},
{479001600.0000000, 1.9987214495661886149228e+01},
{6227020800.000000, 2.2552163853123422886104e+01},
{87178291200.00000, 2.5191221182738681499610e+01},
{1307674368000.000, 2.7899271383840891566988e+01},
{20922789888000.00, 3.0671860106080672803835e+01},
{355687428096000.0, 3.3505073450136888885825e+01},
{6402373705728000., 3.6395445208033053576674e+01}
};
#define FactTableLength (int)(sizeof(FactTable)/sizeof(FactTable[0]))
// for speed
static const double ln_2pi_2 = 0.918938533204672741803; // log(2*PI)/2
/* A simple lgamma function, not very robust.
Valid for z_in > 0 ONLY.
For z_in > 8 precision is quite good, relative errors < 1e-14 and
usually better. For z_in < 8 relative errors increase but are usually
< 1e-10. In two small regions, 1 +/- .001 and 2 +/- .001 errors
increase quickly.
*/
static double nsLnGamma (double z_in, int *gsign)
{
double scale, z, sum, result;
*gsign = 1;
int zi = (int) z_in;
if (z_in == (double) zi)
{
if (0 < zi && zi <= FactTableLength)
return FactTable[zi - 1].lnfact; // gamma(z) = (z-1)!
}
for (scale = 1.0, z = z_in; z < 8.0; ++z)
scale *= z;
sum = lngamma_asymp (z);
result = (z - 0.5) * log (z) - z + ln_2pi_2 - log (scale);
result += sum;
return result;
}
// log( e^(-x)*x^a/Gamma(a) )
static inline double lnPQfactor (double a, double x)
{
int gsign; // ignored because a > 0
return a * log (x) - x - nsLnGamma (a, &gsign);
}
static double Pseries (double a, double x, int *error)
{
double sum, term;
const double eps = 2.0 * DBL_EPSILON;
const int imax = 5000;
int i;
sum = term = 1.0 / a;
for (i = 1; i < imax; ++i)
{
term *= x / (a + i);
sum += term;
if (fabs (term) < eps * fabs (sum))
break;
}
if (i >= imax)
*error = 1;
return sum;
}
static double Qcontfrac (double a, double x, int *error)
{
double result, D, C, e, f, term;
const double eps = 2.0 * DBL_EPSILON;
const double small =
DBL_EPSILON * DBL_EPSILON * DBL_EPSILON * DBL_EPSILON;
const int imax = 5000;
int i;
// modified Lentz method
f = x - a + 1.0;
if (fabs (f) < small)
f = small;
C = f + 1.0 / small;
D = 1.0 / f;
result = D;
for (i = 1; i < imax; ++i)
{
e = i * (a - i);
f += 2.0;
D = f + e * D;
if (fabs (D) < small)
D = small;
D = 1.0 / D;
C = f + e / C;
if (fabs (C) < small)
C = small;
term = C * D;
result *= term;
if (fabs (term - 1.0) < eps)
break;
}
if (i >= imax)
*error = 1;
return result;
}
static double nsIncompleteGammaP (double a, double x, int *error)
{
double result, dom, ldom;
// domain errors. the return values are meaningless but have
// to return something.
*error = -1;
if (a <= 0.0)
return 1.0;
if (x < 0.0)
return 0.0;
*error = 0;
if (x == 0.0)
return 0.0;
ldom = lnPQfactor (a, x);
dom = exp (ldom);
// might need to adjust the crossover point
if (a <= 0.5)
{
if (x < a + 1.0)
result = dom * Pseries (a, x, error);
else
result = 1.0 - dom * Qcontfrac (a, x, error);
}
else
{
if (x < a)
result = dom * Pseries (a, x, error);
else
result = 1.0 - dom * Qcontfrac (a, x, error);
}
// not clear if this can ever happen
if (result > 1.0)
result = 1.0;
if (result < 0.0)
result = 0.0;
return result;
}
#endif

View file

@ -0,0 +1,36 @@
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var useCustomPrefs;
var requestAlways;
var gIdentity;
function onInit()
{
useCustomPrefs = document.getElementById("identity.dsn_use_custom_prefs");
requestAlways = document.getElementById("identity.dsn_always_request_on");
EnableDisableCustomSettings();
return true;
}
function onSave()
{
}
function EnableDisableCustomSettings() {
if (useCustomPrefs && (useCustomPrefs.getAttribute("value") == "false"))
requestAlways.setAttribute("disabled", "true");
else
requestAlways.removeAttribute("disabled");
return true;
}
function onPreInit(account, accountValues)
{
gIdentity = account.defaultIdentity;
}

View file

@ -0,0 +1,57 @@
<?xml version="1.0"?>
<!--
This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://messenger/skin/accountManage.css" type="text/css"?>
<!DOCTYPE page SYSTEM "chrome://messenger/locale/am-dsn.dtd">
<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="parent.onPanelLoaded('am-dsn.xul');">
<stringbundle id="bundle_smime" src="chrome://messenger/locale/am-dsn.properties"/>
<script type="application/javascript" src="chrome://messenger/content/AccountManager.js"/>
<script type="application/javascript" src="chrome://messenger/content/am-dsn.js"/>
<dialogheader title="&pane.title;"/>
<groupbox>
<caption label="&pane.title;"/>
<hbox id="prefChoices" align="center">
<radiogroup id="identity.dsn_use_custom_prefs"
wsm_persist="true"
genericattr="true"
preftype="bool"
prefstring="mail.identity.%identitykey%.dsn_use_custom_prefs"
oncommand="EnableDisableCustomSettings();">
<radio id="identity.select_global_prefs"
value="false"
label="&useGlobalPrefs.label;"
accesskey="&useGlobalPrefs.accesskey;"/>
<radio id="identity.select_custom_prefs"
value="true"
label="&useCustomPrefs.label;"
accesskey="&useCustomPrefs.accesskey;"/>
</radiogroup>
</hbox>
<vbox id="dsnSettings" class="indent" align="start">
<checkbox id="identity.dsn_always_request_on"
label="&requestAlways.label;"
accesskey="&requestAlways.accesskey;"
wsm_persist="true"
genericattr="true"
iscontrolcontainer="true"
preftype="bool"
prefstring="mail.identity.%identitykey%.dsn_always_request_on"/>
</vbox>
</groupbox>
</page>

View file

@ -0,0 +1,9 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* default prefs for dsn
*/
pref("mail.identity.default.dsn_use_custom_prefs", false); // false: Use global true: Use custom
pref("mail.identity.default.dsn_always_request_on", false);

View file

@ -0,0 +1,9 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifdef MOZ_SUITE
messenger.jar:
content/messenger/am-dsn.xul (content/am-dsn.xul)
content/messenger/am-dsn.js (content/am-dsn.js)
#endif

View file

@ -0,0 +1,15 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXTRA_COMPONENTS += [
'src/dsn-service.js',
'src/dsn-service.manifest',
]
JAR_MANIFESTS += ['jar.mn']
JS_PREFERENCE_FILES += [
'content/dsn.js',
]

View file

@ -0,0 +1,24 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
function DSNService() {}
DSNService.prototype = {
name: "dsn",
chromePackageName: "messenger",
showPanel: function(server) {
// don't show the panel for news, rss, or local accounts
return (server.type != "nntp" && server.type != "rss" &&
server.type != "none");
},
QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIMsgAccountManagerExtension]),
classID: Components.ID("{849dab91-9bc9-4508-a0ee-c2453e7c092d}"),
};
var components = [DSNService];
var NSGetFactory = XPCOMUtils.generateNSGetFactory(components);

View file

@ -0,0 +1,3 @@
component {849dab91-9bc9-4508-a0ee-c2453e7c092d} dsn-service.js
contract @mozilla.org/accountmanager/extension;1?name=dsn {849dab91-9bc9-4508-a0ee-c2453e7c092d}
category mailnews-accountmanager-extensions dsn-account-manager-extension @mozilla.org/accountmanager/extension;1?name=dsn

View file

@ -0,0 +1,5 @@
The data files in this directory come from the ICU project:
http://bugs.icu-project.org/trac/browser/icu/trunk/source/data/unidata/norm2
They are intended to be consumed by the ICU project's gennorm2 script. We have
our own script that processes them.

View file

@ -0,0 +1,264 @@
#!/usr/bin/python
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Mozilla Thunderbird.
#
# The Initial Developer of the Original Code is Mozilla Japan.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Makoto Kato <m_kato@ga2.so-net.ne.jp>
# Andrew Sutherland <asutherland@asutherland.org>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
import re
def printTable(f, t):
i = f
while i <= t:
c = array[i]
print "0x%04x," % c,
i = i + 1
if not i % 8:
print "\n\t",
print '''/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Mozilla Japan.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Makoto Kato <m_kato@ga2.so-net.ne.jp>
* Andrew Sutherland <asutherland@asutherland.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/* THIS FILE IS GENERATED BY generate_table.py. DON'T EDIT THIS */
'''
p = re.compile('([0-9A-F]{4,5})(?:\.\.([0-9A-F]{4,5}))?[=\>]([0-9A-F]{4,5})?')
G_FROM = 1
G_TO = 2
G_FIRSTVAL = 3
# Array whose value at index i is the unicode value unicode character i should
# map to.
array = []
# Contents of gNormalizeTable. We insert zero entries for sub-pages where we
# have no mappings. We insert references to the tables where we do have
# such tables.
globalTable = []
globalTable.append("0")
# The (exclusive) upper bound of the conversion table, unicode character-wise.
# This is 0x10000 because our generated table is only 16-bit. This also limits
# the values we can map to; we perform an identity mapping for target values
# that >= maxmapping.
maxmapping = 0x10000
sizePerTable = 64
# Map characters that the mapping tells us to obliterate to the NUKE_CHAR
# (such lines look like "FFF0..FFF8>")
# We do this because if we didn't do this, we would emit these characters as
# part of a token, which we definitely don't want.
NUKE_CHAR = 0x20
# --- load case folding table
# entries in the file look like:
# 0041>0061
# 02D8>0020 0306
# 2000..200A>0020
#
# The 0041 (uppercase A) tells us it lowercases to 0061 (lowercase a).
# The 02D8 is a "spacing clone[s] of diacritic" breve which gets decomposed into
# a space character and a breve. This entry/type of entry also shows up in
# 'nfkc.txt'.
# The 2000..200A covers a range of space characters and maps them down to the
# 'normal' space character.
file = open('nfkc_cf.txt')
m = None
line = "\n"
i = 0x0
while i < maxmapping and line:
if not m:
line = file.readline()
m = p.match(line)
if not m:
continue
low = int(m.group(G_FROM), 16)
# if G_TO is present, use it, otherwise fallback to low
high = m.group(G_TO) and int(m.group(G_TO), 16) or low
# if G_FIRSTVAL is present use it, otherwise use NUKE_CHAR
val = (m.group(G_FIRSTVAL) and int(m.group(G_FIRSTVAL), 16)
or NUKE_CHAR)
continue
if i >= low and i <= high:
if val >= maxmapping:
array.append(i)
else:
array.append(val)
if i == high:
m = None
else:
array.append(i)
i = i + 1
file.close()
# --- load normalization / decomposition table
# It is important that this file gets processed second because the other table
# will tell us about mappings from uppercase U with diaeresis to lowercase u
# with diaeresis. We obviously don't want that clobbering our value. (Although
# this would work out if we propagated backwards rather than forwards...)
#
# - entries in this file that we care about look like:
# 00A0>0020
# 0100=0041 0304
#
# They are found in the "Canonical and compatibility decomposition mappings"
# section.
#
# The 00A0 is mapping NBSP to the normal space character.
# The 0100 (a capital A with a bar over top of) is equivalent to 0041 (capital
# A) plus a 0304 (combining overline). We do not care about the combining
# marks which is why our regular expression does not capture it.
#
#
# - entries that we do not care about look like:
# 0300..0314:230
#
# These map marks to their canonical combining class which appears to be a way
# of specifying the precedence / order in which marks should be combined. The
# key thing is we don't care about them.
file = open('nfkc.txt')
line = file.readline()
m = p.match(line)
while line:
if not m:
line = file.readline()
m = p.match(line)
continue
low = int(m.group(G_FROM), 16)
# if G_TO is present, use it, otherwise fallback to low
high = m.group(G_TO) and int(m.group(G_TO), 16) or low
# if G_FIRSTVAL is present use it, otherwise fall back to NUKE_CHAR
val = m.group(G_FIRSTVAL) and int(m.group(G_FIRSTVAL), 16) or NUKE_CHAR
for i in range(low, high+1):
if i < maxmapping and val < maxmapping:
array[i] = val
m = None
file.close()
# --- generate a normalized table to support case and accent folding
i = 0
needTerm = False;
while i < maxmapping:
if not i % sizePerTable:
# table is empty?
j = i
while j < i + sizePerTable:
if array[j] != j:
break
j += 1
if j == i + sizePerTable:
if i:
globalTable.append("0")
i += sizePerTable
continue
if needTerm:
print "};\n"
globalTable.append("gNormalizeTable%04x" % i)
print "static const unsigned short gNormalizeTable%04x[] = {\n\t" % i,
print "/* U+%04x */\n\t" % i,
needTerm = True
# Decomposition does not case-fold, so we want to compensate by
# performing a lookup here. Because decomposition chains can be
# example: 01d5, a capital U with a diaeresis and a bar. yes, really.
# 01d5 -> 00dc -> 0055 (U) -> 0075 (u)
c = array[i]
while c != array[c]:
c = array[c]
if c >= 0x41 and c <= 0x5a:
raise Exception('got an uppercase character somehow: %x => %x'
% (i, c))
print "0x%04x," % c,
i = i + 1
if not i % 8:
print "\n\t",
print "};\n\nstatic const unsigned short* gNormalizeTable[] = {",
i = 0
while i < (maxmapping / sizePerTable):
if not i % 4:
print "\n\t",
print globalTable[i] + ",",
i += 1
print '''
};
unsigned int normalize_character(const unsigned int c)
{
if (c >= ''' + ('0x%x' % (maxmapping,)) + ''' || !gNormalizeTable[c >> 6])
return c;
return gNormalizeTable[c >> 6][c & 0x3f];
}
'''

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,11 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
XPIDL_SOURCES += [
'nsIFts3Tokenizer.idl',
]
XPIDL_MODULE = 'fts3tok'

View file

@ -0,0 +1,15 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface mozIStorageConnection;
[scriptable, uuid(136c88ea-7003-4fe8-8835-333fd18e598c)]
interface nsIFts3Tokenizer : nsISupports {
// register FTS3 tokenizer module for "mozporter" tokenizer
// mozporter is based by porter tokenizer with bi-gram tokenizer for CJK
void registerTokenizer(in mozIStorageConnection connection);
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,3 @@
fts3_porter.c code is from SQLite3.
This customized tokenizer "mozporter" by Mozilla supports CJK indexing using bi-gram. So you have to use bi-gram search string if you wanto to search CJK character.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,148 @@
/*
** 2006 July 10
**
** The author disclaims copyright to this source code.
**
*************************************************************************
** Defines the interface to tokenizers used by fulltext-search. There
** are three basic components:
**
** sqlite3_tokenizer_module is a singleton defining the tokenizer
** interface functions. This is essentially the class structure for
** tokenizers.
**
** sqlite3_tokenizer is used to define a particular tokenizer, perhaps
** including customization information defined at creation time.
**
** sqlite3_tokenizer_cursor is generated by a tokenizer to generate
** tokens from a particular input.
*/
#ifndef _FTS3_TOKENIZER_H_
#define _FTS3_TOKENIZER_H_
/* TODO(shess) Only used for SQLITE_OK and SQLITE_DONE at this time.
** If tokenizers are to be allowed to call sqlite3_*() functions, then
** we will need a way to register the API consistently.
*/
#include "sqlite3.h"
/*
** Structures used by the tokenizer interface. When a new tokenizer
** implementation is registered, the caller provides a pointer to
** an sqlite3_tokenizer_module containing pointers to the callback
** functions that make up an implementation.
**
** When an fts3 table is created, it passes any arguments passed to
** the tokenizer clause of the CREATE VIRTUAL TABLE statement to the
** sqlite3_tokenizer_module.xCreate() function of the requested tokenizer
** implementation. The xCreate() function in turn returns an
** sqlite3_tokenizer structure representing the specific tokenizer to
** be used for the fts3 table (customized by the tokenizer clause arguments).
**
** To tokenize an input buffer, the sqlite3_tokenizer_module.xOpen()
** method is called. It returns an sqlite3_tokenizer_cursor object
** that may be used to tokenize a specific input buffer based on
** the tokenization rules supplied by a specific sqlite3_tokenizer
** object.
*/
typedef struct sqlite3_tokenizer_module sqlite3_tokenizer_module;
typedef struct sqlite3_tokenizer sqlite3_tokenizer;
typedef struct sqlite3_tokenizer_cursor sqlite3_tokenizer_cursor;
struct sqlite3_tokenizer_module {
/*
** Structure version. Should always be set to 0.
*/
int iVersion;
/*
** Create a new tokenizer. The values in the argv[] array are the
** arguments passed to the "tokenizer" clause of the CREATE VIRTUAL
** TABLE statement that created the fts3 table. For example, if
** the following SQL is executed:
**
** CREATE .. USING fts3( ... , tokenizer <tokenizer-name> arg1 arg2)
**
** then argc is set to 2, and the argv[] array contains pointers
** to the strings "arg1" and "arg2".
**
** This method should return either SQLITE_OK (0), or an SQLite error
** code. If SQLITE_OK is returned, then *ppTokenizer should be set
** to point at the newly created tokenizer structure. The generic
** sqlite3_tokenizer.pModule variable should not be initialised by
** this callback. The caller will do so.
*/
int (*xCreate)(
int argc, /* Size of argv array */
const char *const*argv, /* Tokenizer argument strings */
sqlite3_tokenizer **ppTokenizer /* OUT: Created tokenizer */
);
/*
** Destroy an existing tokenizer. The fts3 module calls this method
** exactly once for each successful call to xCreate().
*/
int (*xDestroy)(sqlite3_tokenizer *pTokenizer);
/*
** Create a tokenizer cursor to tokenize an input buffer. The caller
** is responsible for ensuring that the input buffer remains valid
** until the cursor is closed (using the xClose() method).
*/
int (*xOpen)(
sqlite3_tokenizer *pTokenizer, /* Tokenizer object */
const char *pInput, int nBytes, /* Input buffer */
sqlite3_tokenizer_cursor **ppCursor /* OUT: Created tokenizer cursor */
);
/*
** Destroy an existing tokenizer cursor. The fts3 module calls this
** method exactly once for each successful call to xOpen().
*/
int (*xClose)(sqlite3_tokenizer_cursor *pCursor);
/*
** Retrieve the next token from the tokenizer cursor pCursor. This
** method should either return SQLITE_OK and set the values of the
** "OUT" variables identified below, or SQLITE_DONE to indicate that
** the end of the buffer has been reached, or an SQLite error code.
**
** *ppToken should be set to point at a buffer containing the
** normalized version of the token (i.e. after any case-folding and/or
** stemming has been performed). *pnBytes should be set to the length
** of this buffer in bytes. The input text that generated the token is
** identified by the byte offsets returned in *piStartOffset and
** *piEndOffset. *piStartOffset should be set to the index of the first
** byte of the token in the input buffer. *piEndOffset should be set
** to the index of the first byte just past the end of the token in
** the input buffer.
**
** The buffer *ppToken is set to point at is managed by the tokenizer
** implementation. It is only required to be valid until the next call
** to xNext() or xClose().
*/
/* TODO(shess) current implementation requires pInput to be
** nul-terminated. This should either be fixed, or pInput/nBytes
** should be converted to zInput.
*/
int (*xNext)(
sqlite3_tokenizer_cursor *pCursor, /* Tokenizer cursor */
const char **ppToken, int *pnBytes, /* OUT: Normalized text for token */
int *piStartOffset, /* OUT: Byte offset of token in input buffer */
int *piEndOffset, /* OUT: Byte offset of end of token in input buffer */
int *piPosition /* OUT: Number of tokens returned before this one */
);
};
struct sqlite3_tokenizer {
const sqlite3_tokenizer_module *pModule; /* The module for this tokenizer */
/* Tokenizer implementations will typically add additional fields */
};
struct sqlite3_tokenizer_cursor {
sqlite3_tokenizer *pTokenizer; /* Tokenizer for this cursor. */
/* Tokenizer implementations will typically add additional fields */
};
#endif /* _FTS3_TOKENIZER_H_ */

View file

@ -0,0 +1,18 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
SOURCES += [
'fts3_porter.c',
'Normalize.c',
]
SOURCES += [
'nsFts3Tokenizer.cpp',
'nsGlodaRankerFunction.cpp',
]
FINAL_LIBRARY = 'mail'
CXXFLAGS += CONFIG['SQLITE_CFLAGS']

View file

@ -0,0 +1,72 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsFts3Tokenizer.h"
#include "nsGlodaRankerFunction.h"
#include "nsIFts3Tokenizer.h"
#include "mozIStorageConnection.h"
#include "mozIStorageStatement.h"
#include "nsStringGlue.h"
extern "C" void sqlite3Fts3PorterTokenizerModule(
sqlite3_tokenizer_module const**ppModule);
extern "C" void glodaRankFunc(sqlite3_context *pCtx,
int nVal,
sqlite3_value **apVal);
NS_IMPL_ISUPPORTS(nsFts3Tokenizer,nsIFts3Tokenizer)
nsFts3Tokenizer::nsFts3Tokenizer()
{
}
nsFts3Tokenizer::~nsFts3Tokenizer()
{
}
NS_IMETHODIMP
nsFts3Tokenizer::RegisterTokenizer(mozIStorageConnection *connection)
{
nsresult rv;
nsCOMPtr<mozIStorageStatement> selectStatement;
// -- register the tokenizer
rv = connection->CreateStatement(NS_LITERAL_CSTRING(
"SELECT fts3_tokenizer(?1, ?2)"),
getter_AddRefs(selectStatement));
NS_ENSURE_SUCCESS(rv, rv);
const sqlite3_tokenizer_module* module = nullptr;
sqlite3Fts3PorterTokenizerModule(&module);
if (!module)
return NS_ERROR_FAILURE;
rv = selectStatement->BindUTF8StringParameter(
0, NS_LITERAL_CSTRING("mozporter"));
NS_ENSURE_SUCCESS(rv, rv);
rv = selectStatement->BindBlobParameter(1,
(uint8_t*)&module,
sizeof(module));
NS_ENSURE_SUCCESS(rv, rv);
bool hasMore;
rv = selectStatement->ExecuteStep(&hasMore);
NS_ENSURE_SUCCESS(rv, rv);
// -- register the ranking function
nsCOMPtr<mozIStorageFunction> func = new nsGlodaRankerFunction();
NS_ENSURE_TRUE(func, NS_ERROR_OUT_OF_MEMORY);
rv = connection->CreateFunction(
NS_LITERAL_CSTRING("glodaRank"),
-1, // variable argument support
func
);
NS_ENSURE_SUCCESS(rv, rv);
return rv;
}

View file

@ -0,0 +1,26 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsFts3Tokenizer_h__
#define nsFts3Tokenizer_h__
#include "nsCOMPtr.h"
#include "nsIFts3Tokenizer.h"
#include "fts3_tokenizer.h"
extern const sqlite3_tokenizer_module* getWindowsTokenizer();
class nsFts3Tokenizer final : public nsIFts3Tokenizer {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIFTS3TOKENIZER
nsFts3Tokenizer();
private:
~nsFts3Tokenizer();
};
#endif

View file

@ -0,0 +1,16 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsFts3TokenizerCID_h__
#define nsFts3TokenizerCID_h__
#define NS_FTS3TOKENIZER_CONTRACTID \
"@mozilla.org/messenger/fts3tokenizer;1"
#define NS_FTS3TOKENIZER_CID \
{ /* a67d724d-0015-4e2e-8cad-b84775330924 */ \
0xa67d724d, 0x0015, 0x4e2e, \
{ 0x8c, 0xad, 0xb8, 0x47, 0x75, 0x33, 0x09, 0x24 }}
#endif /* nsFts3TokenizerCID_h__ */

View file

@ -0,0 +1,145 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Thunderbird Global Database.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Andrew Sutherland <asutherland@asutherland.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsGlodaRankerFunction.h"
#include "mozIStorageValueArray.h"
#include "sqlite3.h"
#include "nsCOMPtr.h"
#include "nsVariant.h"
#include "nsComponentManagerUtils.h"
#ifndef SQLITE_VERSION_NUMBER
#error "We need SQLITE_VERSION_NUMBER defined!"
#endif
NS_IMPL_ISUPPORTS(nsGlodaRankerFunction, mozIStorageFunction)
nsGlodaRankerFunction::nsGlodaRankerFunction()
{
}
nsGlodaRankerFunction::~nsGlodaRankerFunction()
{
}
static uint32_t COLUMN_SATURATION[] = {10, 1, 1, 1, 1};
/**
* Our ranking function basically just multiplies the weight of the column
* against the number of (saturating) matches.
*
* The original code is a SQLite example ranking function, although somewhat
* rather modified at this point. All SQLite code is public domain, so we are
* subsuming it to MPL1.1/LGPL2/GPL2.
*/
NS_IMETHODIMP
nsGlodaRankerFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
nsIVariant **_result)
{
// all argument names are maintained from the original SQLite code.
uint32_t nVal;
nsresult rv = aArguments->GetNumEntries(&nVal);
NS_ENSURE_SUCCESS(rv, rv);
/* Check that the number of arguments passed to this function is correct.
* If not, return an error. Set aArgsData to point to the array
* of unsigned integer values returned by FTS3 function. Set nPhrase
* to contain the number of reportable phrases in the users full-text
* query, and nCol to the number of columns in the table.
*/
if (nVal < 1)
return NS_ERROR_INVALID_ARG;
uint32_t lenArgsData;
uint32_t *aArgsData = (uint32_t *)aArguments->AsSharedBlob(0, &lenArgsData);
uint32_t nPhrase = aArgsData[0];
uint32_t nCol = aArgsData[1];
if (nVal != (1 + nCol))
return NS_ERROR_INVALID_ARG;
double score = 0.0;
// SQLite 3.6.22 has a different matchinfo layout than SQLite 3.6.23+
#if SQLITE_VERSION_NUMBER <= 3006022
/* Iterate through each phrase in the users query. */
for (uint32_t iPhrase = 0; iPhrase < nPhrase; iPhrase++) {
// in SQ
for (uint32_t iCol = 0; iCol < nCol; iCol++) {
uint32_t nHitCount = aArgsData[2 + (iPhrase+1)*nCol + iCol];
double weight = aArguments->AsDouble(iCol+1);
if (nHitCount > 0) {
score += (nHitCount > COLUMN_SATURATION[iCol]) ?
(COLUMN_SATURATION[iCol] * weight) :
(nHitCount * weight);
}
}
}
#else
/* Iterate through each phrase in the users query. */
for (uint32_t iPhrase = 0; iPhrase < nPhrase; iPhrase++) {
/* Now iterate through each column in the users query. For each column,
** increment the relevancy score by:
**
** (<hit count> / <global hit count>) * <column weight>
**
** aPhraseinfo[] points to the start of the data for phrase iPhrase. So
** the hit count and global hit counts for each column are found in
** aPhraseinfo[iCol*3] and aPhraseinfo[iCol*3+1], respectively.
*/
uint32_t *aPhraseinfo = &aArgsData[2 + iPhrase*nCol*3];
for (uint32_t iCol = 0; iCol < nCol; iCol++) {
uint32_t nHitCount = aPhraseinfo[3 * iCol];
double weight = aArguments->AsDouble(iCol+1);
if (nHitCount > 0) {
score += (nHitCount > COLUMN_SATURATION[iCol]) ?
(COLUMN_SATURATION[iCol] * weight) :
(nHitCount * weight);
}
}
}
#endif
nsCOMPtr<nsIWritableVariant> result = new nsVariant();
rv = result->SetAsDouble(score);
NS_ENSURE_SUCCESS(rv, rv);
NS_ADDREF(*_result = result);
return NS_OK;
}

View file

@ -0,0 +1,25 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _nsGlodaRankerFunction_h_
#define _nsGlodaRankerFunction_h_
#include "mozIStorageFunction.h"
/**
* Basically a port of the example FTS3 ranking function to mozStorage's
* view of the universe. This might get fancier at some point.
*/
class nsGlodaRankerFunction final : public mozIStorageFunction
{
public:
NS_DECL_ISUPPORTS
NS_DECL_MOZISTORAGEFUNCTION
nsGlodaRankerFunction();
private:
~nsGlodaRankerFunction();
};
#endif // _nsGlodaRankerFunction_h_

View file

@ -0,0 +1,22 @@
version="8"
logging="no"
name="People I Know"
enabled="yes"
type="1"
condition="AND (from,is in ab,moz-abmdbdirectory://abook.mab)"
name="Recent Mail"
enabled="yes"
type="1"
condition="AND (age in days,is less than,1)"
name="Last 5 Days"
enabled="yes"
type="1"
condition="AND (age in days,is less than,5)"
name="Not Junk"
enabled="yes"
type="1"
condition="AND (junk status,isn't,2)"
name="Has Attachments"
enabled="yes"
type="1"
condition="AND (has attachment status,is,true)"

View file

@ -0,0 +1,8 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
FINAL_TARGET_FILES.defaults.messenger += [
'mailViews.dat',
]

View file

@ -0,0 +1,12 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
XPIDL_SOURCES += [
'nsIMsgMailView.idl',
'nsIMsgMailViewList.idl',
]
XPIDL_MODULE = 'mailview'

View file

@ -0,0 +1,35 @@
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
// Disable deprecation warnings generated by nsISupportsArray and associated
// classes.
%{C++
#if defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#elif defined(_MSC_VER)
#pragma warning (disable : 4996)
#endif
%}
interface nsISupportsArray;
interface nsIMsgSearchTerm;
[scriptable, uuid(28AC84DF-CBE5-430d-A5C0-4FA63B5424DF)]
interface nsIMsgMailView : nsISupports {
attribute wstring mailViewName;
readonly attribute wstring prettyName; // localized pretty name
// the array of search terms
attribute nsISupportsArray searchTerms;
// these two helper methods are required to allow searchTermsOverlay.js to
// manipulate a mail view without knowing it is dealing with a mail view. nsIMsgFilter
// and nsIMsgSearchSession have the same two methods....we should probably make an interface around them.
void appendTerm(in nsIMsgSearchTerm term);
nsIMsgSearchTerm createTerm();
};

View file

@ -0,0 +1,28 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
#include "nsIMsgMailView.idl"
///////////////////////////////////////////////////////////////////////////////
// A mail view list is a list of mail views a particular implementor provides
///////////////////////////////////////////////////////////////////////////////
typedef long nsMsgMailViewListFileAttribValue;
[scriptable, uuid(6DD798D7-9528-49e6-9447-3AAF14D2D36F)]
interface nsIMsgMailViewList : nsISupports {
readonly attribute unsigned long mailViewCount;
nsIMsgMailView getMailViewAt(in unsigned long mailViewIndex);
void addMailView(in nsIMsgMailView mailView);
void removeMailView(in nsIMsgMailView mailView);
nsIMsgMailView createMailView();
void save();
};

View file

@ -0,0 +1,11 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
SOURCES += [
'nsMsgMailViewList.cpp',
]
FINAL_LIBRARY = 'mail'

View file

@ -0,0 +1,312 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsMsgMailViewList.h"
// Disable deprecation warnings generated by nsISupportsArray and associated
// classes.
#if defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#elif defined(_MSC_VER)
#pragma warning (disable : 4996)
#endif
#include "nsISupportsArray.h"
#include "nsIFileChannel.h"
#include "nsIMsgFilterService.h"
#include "nsIMsgMailSession.h"
#include "nsIMsgSearchTerm.h"
#include "nsMsgBaseCID.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "nsIFile.h"
#include "nsComponentManagerUtils.h"
#include "mozilla/Services.h"
#include "nsIMsgFilter.h"
#define kDefaultViewPeopleIKnow "People I Know"
#define kDefaultViewRecent "Recent Mail"
#define kDefaultViewFiveDays "Last 5 Days"
#define kDefaultViewNotJunk "Not Junk"
#define kDefaultViewHasAttachments "Has Attachments"
nsMsgMailView::nsMsgMailView()
{
mViewSearchTerms = do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID);
}
NS_IMPL_ADDREF(nsMsgMailView)
NS_IMPL_RELEASE(nsMsgMailView)
NS_IMPL_QUERY_INTERFACE(nsMsgMailView, nsIMsgMailView)
nsMsgMailView::~nsMsgMailView()
{
if (mViewSearchTerms)
mViewSearchTerms->Clear();
}
NS_IMETHODIMP nsMsgMailView::GetMailViewName(char16_t ** aMailViewName)
{
NS_ENSURE_ARG_POINTER(aMailViewName);
*aMailViewName = ToNewUnicode(mName);
return NS_OK;
}
NS_IMETHODIMP nsMsgMailView::SetMailViewName(const char16_t * aMailViewName)
{
mName = aMailViewName;
return NS_OK;
}
NS_IMETHODIMP nsMsgMailView::GetPrettyName(char16_t ** aMailViewName)
{
NS_ENSURE_ARG_POINTER(aMailViewName);
nsresult rv = NS_OK;
if (!mBundle)
{
nsCOMPtr<nsIStringBundleService> bundleService =
mozilla::services::GetStringBundleService();
NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
bundleService->CreateBundle("chrome://messenger/locale/mailviews.properties",
getter_AddRefs(mBundle));
}
NS_ENSURE_TRUE(mBundle, NS_ERROR_FAILURE);
// see if mName has an associated pretty name inside our string bundle and if so, use that as the pretty name
// otherwise just return mName
if (mName.EqualsLiteral(kDefaultViewPeopleIKnow))
rv = mBundle->GetStringFromName(u"mailViewPeopleIKnow", aMailViewName);
else if (mName.EqualsLiteral(kDefaultViewRecent))
rv = mBundle->GetStringFromName(u"mailViewRecentMail", aMailViewName);
else if (mName.EqualsLiteral(kDefaultViewFiveDays))
rv = mBundle->GetStringFromName(u"mailViewLastFiveDays", aMailViewName);
else if (mName.EqualsLiteral(kDefaultViewNotJunk))
rv = mBundle->GetStringFromName(u"mailViewNotJunk", aMailViewName);
else if (mName.EqualsLiteral(kDefaultViewHasAttachments))
rv = mBundle->GetStringFromName(u"mailViewHasAttachments", aMailViewName);
else
*aMailViewName = ToNewUnicode(mName);
return rv;
}
NS_IMETHODIMP nsMsgMailView::GetSearchTerms(nsISupportsArray ** aSearchTerms)
{
NS_ENSURE_ARG_POINTER(aSearchTerms);
NS_IF_ADDREF(*aSearchTerms = mViewSearchTerms);
return NS_OK;
}
NS_IMETHODIMP nsMsgMailView::SetSearchTerms(nsISupportsArray * aSearchTerms)
{
mViewSearchTerms = aSearchTerms;
return NS_OK;
}
NS_IMETHODIMP nsMsgMailView::AppendTerm(nsIMsgSearchTerm * aTerm)
{
NS_ENSURE_TRUE(aTerm, NS_ERROR_NULL_POINTER);
return mViewSearchTerms->AppendElement(static_cast<nsISupports*>(aTerm));
}
NS_IMETHODIMP nsMsgMailView::CreateTerm(nsIMsgSearchTerm **aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
nsCOMPtr<nsIMsgSearchTerm> searchTerm = do_CreateInstance("@mozilla.org/messenger/searchTerm;1");
NS_IF_ADDREF(*aResult = searchTerm);
return NS_OK;
}
/////////////////////////////////////////////////////////////////////////////
// nsMsgMailViewList implementation
/////////////////////////////////////////////////////////////////////////////
nsMsgMailViewList::nsMsgMailViewList()
{
LoadMailViews();
}
NS_IMPL_ADDREF(nsMsgMailViewList)
NS_IMPL_RELEASE(nsMsgMailViewList)
NS_IMPL_QUERY_INTERFACE(nsMsgMailViewList, nsIMsgMailViewList)
nsMsgMailViewList::~nsMsgMailViewList()
{
}
NS_IMETHODIMP nsMsgMailViewList::GetMailViewCount(uint32_t * aCount)
{
NS_ENSURE_ARG_POINTER(aCount);
*aCount = m_mailViews.Length();
return NS_OK;
}
NS_IMETHODIMP nsMsgMailViewList::GetMailViewAt(uint32_t aMailViewIndex, nsIMsgMailView ** aMailView)
{
NS_ENSURE_ARG_POINTER(aMailView);
uint32_t mailViewCount = m_mailViews.Length();
NS_ENSURE_ARG(mailViewCount > aMailViewIndex);
NS_IF_ADDREF(*aMailView = m_mailViews[aMailViewIndex]);
return NS_OK;
}
NS_IMETHODIMP nsMsgMailViewList::AddMailView(nsIMsgMailView * aMailView)
{
NS_ENSURE_ARG_POINTER(aMailView);
m_mailViews.AppendElement(aMailView);
return NS_OK;
}
NS_IMETHODIMP nsMsgMailViewList::RemoveMailView(nsIMsgMailView * aMailView)
{
NS_ENSURE_ARG_POINTER(aMailView);
m_mailViews.RemoveElement(aMailView);
return NS_OK;
}
NS_IMETHODIMP nsMsgMailViewList::CreateMailView(nsIMsgMailView ** aMailView)
{
NS_ENSURE_ARG_POINTER(aMailView);
nsMsgMailView * mailView = new nsMsgMailView;
NS_ENSURE_TRUE(mailView, NS_ERROR_OUT_OF_MEMORY);
NS_IF_ADDREF(*aMailView = mailView);
return NS_OK;
}
NS_IMETHODIMP nsMsgMailViewList::Save()
{
// brute force...remove all the old filters in our filter list, then we'll re-add our current
// list
nsCOMPtr<nsIMsgFilter> msgFilter;
uint32_t numFilters = 0;
if (mFilterList)
mFilterList->GetFilterCount(&numFilters);
while (numFilters)
{
mFilterList->RemoveFilterAt(numFilters - 1);
numFilters--;
}
// now convert our mail view list into a filter list and save it
ConvertMailViewListToFilterList();
// now save the filters to our file
return mFilterList ? mFilterList->SaveToDefaultFile() : NS_ERROR_FAILURE;
}
nsresult nsMsgMailViewList::ConvertMailViewListToFilterList()
{
uint32_t mailViewCount = m_mailViews.Length();
nsCOMPtr<nsIMsgMailView> mailView;
nsCOMPtr<nsIMsgFilter> newMailFilter;
nsString mailViewName;
for (uint32_t index = 0; index < mailViewCount; index++)
{
GetMailViewAt(index, getter_AddRefs(mailView));
if (!mailView)
continue;
mailView->GetMailViewName(getter_Copies(mailViewName));
mFilterList->CreateFilter(mailViewName, getter_AddRefs(newMailFilter));
if (!newMailFilter)
continue;
nsCOMPtr<nsISupportsArray> searchTerms;
mailView->GetSearchTerms(getter_AddRefs(searchTerms));
newMailFilter->SetSearchTerms(searchTerms);
mFilterList->InsertFilterAt(index, newMailFilter);
}
return NS_OK;
}
nsresult nsMsgMailViewList::LoadMailViews()
{
nsCOMPtr<nsIFile> file;
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
NS_ENSURE_SUCCESS(rv, rv);
rv = file->AppendNative(nsDependentCString("mailViews.dat"));
// if the file doesn't exist, we should try to get it from the defaults directory and copy it over
bool exists = false;
file->Exists(&exists);
if (!exists)
{
nsCOMPtr<nsIMsgMailSession> mailSession = do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIFile> defaultMessagesFile;
nsCOMPtr<nsIFile> profileDir;
rv = mailSession->GetDataFilesDir("messenger", getter_AddRefs(defaultMessagesFile));
rv = defaultMessagesFile->AppendNative(nsDependentCString("mailViews.dat"));
// get the profile directory
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(profileDir));
// now copy the file over to the profile directory
defaultMessagesFile->CopyToNative(profileDir, EmptyCString());
}
// this is kind of a hack but I think it will be an effective hack. The filter service already knows how to
// take a nsIFile and parse the contents into filters which are very similar to mail views. Intead of
// re-writing all of that dirty parsing code, let's just re-use it then convert the results into a data strcuture
// we wish to give to our consumers.
nsCOMPtr<nsIMsgFilterService> filterService = do_GetService(NS_MSGFILTERSERVICE_CONTRACTID, &rv);
nsCOMPtr<nsIMsgFilterList> mfilterList;
rv = filterService->OpenFilterList(file, nullptr, nullptr, getter_AddRefs(mFilterList));
NS_ENSURE_SUCCESS(rv, rv);
return ConvertFilterListToMailViews();
}
/**
* Converts the filter list into our mail view objects,
* stripping out just the info we need.
*/
nsresult nsMsgMailViewList::ConvertFilterListToMailViews()
{
nsresult rv = NS_OK;
m_mailViews.Clear();
// iterate over each filter in the list
uint32_t numFilters = 0;
mFilterList->GetFilterCount(&numFilters);
for (uint32_t index = 0; index < numFilters; index++)
{
nsCOMPtr<nsIMsgFilter> msgFilter;
rv = mFilterList->GetFilterAt(index, getter_AddRefs(msgFilter));
if (NS_FAILED(rv) || !msgFilter)
continue;
// create a new nsIMsgMailView for this item
nsCOMPtr<nsIMsgMailView> newMailView;
rv = CreateMailView(getter_AddRefs(newMailView));
NS_ENSURE_SUCCESS(rv, rv);
nsString filterName;
msgFilter->GetFilterName(filterName);
newMailView->SetMailViewName(filterName.get());
nsCOMPtr<nsISupportsArray> filterSearchTerms;
rv = msgFilter->GetSearchTerms(getter_AddRefs(filterSearchTerms));
NS_ENSURE_SUCCESS(rv, rv);
rv = newMailView->SetSearchTerms(filterSearchTerms);
NS_ENSURE_SUCCESS(rv, rv);
// now append this new mail view to our global list view
m_mailViews.AppendElement(newMailView);
}
return rv;
}

View file

@ -0,0 +1,61 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _nsMsgMailViewList_H_
#define _nsMsgMailViewList_H_
#include "nscore.h"
#include "nsIMsgMailViewList.h"
#include "nsCOMPtr.h"
#include "nsCOMArray.h"
// Disable deprecation warnings generated by nsISupportsArray and associated
// classes.
#if defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#elif defined(_MSC_VER)
#pragma warning (disable : 4996)
#endif
#include "nsISupportsArray.h"
#include "nsIStringBundle.h"
#include "nsStringGlue.h"
#include "nsIMsgFilterList.h"
// a mail View is just a name and an array of search terms
class nsMsgMailView : public nsIMsgMailView
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIMSGMAILVIEW
nsMsgMailView();
protected:
virtual ~nsMsgMailView();
nsString mName;
nsCOMPtr<nsIStringBundle> mBundle;
nsCOMPtr<nsISupportsArray> mViewSearchTerms;
};
class nsMsgMailViewList : public nsIMsgMailViewList
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIMSGMAILVIEWLIST
nsMsgMailViewList();
protected:
virtual ~nsMsgMailViewList();
nsresult LoadMailViews(); // reads in user defined mail views from our default file
nsresult ConvertFilterListToMailViews();
nsresult ConvertMailViewListToFilterList();
nsCOMArray<nsIMsgMailView> m_mailViews;
nsCOMPtr<nsIMsgFilterList> mFilterList; // our internal filter list representation
};
#endif

View file

@ -0,0 +1,17 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsMsgMailViewsCID_h__
#define nsMsgMailViewsCID_h__
#define NS_MSGMAILVIEWLIST_CONTRACTID \
"@mozilla.org/messenger/mailviewlist;1"
#define NS_MSGMAILVIEWLIST_CID \
{ /* A0258267-44FD-4886-A858-8192615178EC */ \
0xa0258267, 0x44fd, 0x4886, \
{ 0xa8, 0x58, 0x81, 0x92, 0x61, 0x51, 0x78, 0xec }}
#endif /* nsMsgMailViewsCID_h__*/

View file

@ -0,0 +1,155 @@
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var useCustomPrefs;
var requestReceipt;
var leaveInInbox;
var moveToSent;
var receiptSend;
var neverReturn;
var returnSome;
var notInToCcPref;
var notInToCcLabel;
var outsideDomainPref;
var outsideDomainLabel;
var otherCasesPref;
var otherCasesLabel;
var receiptArriveLabel;
var receiptRequestLabel;
var gIdentity;
var gIncomingServer;
var gMdnPrefBranch;
function onInit()
{
useCustomPrefs = document.getElementById("identity.use_custom_prefs");
requestReceipt = document.getElementById("identity.request_return_receipt_on");
leaveInInbox = document.getElementById("leave_in_inbox");
moveToSent = document.getElementById("move_to_sent");
receiptSend = document.getElementById("server.mdn_report_enabled");
neverReturn = document.getElementById("never_return");
returnSome = document.getElementById("return_some");
notInToCcPref = document.getElementById("server.mdn_not_in_to_cc");
notInToCcLabel = document.getElementById("notInToCcLabel");
outsideDomainPref = document.getElementById("server.mdn_outside_domain");
outsideDomainLabel = document.getElementById("outsideDomainLabel");
otherCasesPref = document.getElementById("server.mdn_other");
otherCasesLabel = document.getElementById("otherCasesLabel");
receiptArriveLabel = document.getElementById("receiptArriveLabel");
receiptRequestLabel = document.getElementById("receiptRequestLabel");
EnableDisableCustomSettings();
return true;
}
function onSave()
{
}
function EnableDisableCustomSettings() {
if (useCustomPrefs && (useCustomPrefs.getAttribute("value") == "false")) {
requestReceipt.setAttribute("disabled", "true");
leaveInInbox.setAttribute("disabled", "true");
moveToSent.setAttribute("disabled", "true");
neverReturn.setAttribute("disabled", "true");
returnSome.setAttribute("disabled", "true");
receiptArriveLabel.setAttribute("disabled", "true");
receiptRequestLabel.setAttribute("disabled", "true");
}
else {
requestReceipt.removeAttribute("disabled");
leaveInInbox.removeAttribute("disabled");
moveToSent.removeAttribute("disabled");
neverReturn.removeAttribute("disabled");
returnSome.removeAttribute("disabled");
receiptArriveLabel.removeAttribute("disabled");
receiptRequestLabel.removeAttribute("disabled");
}
EnableDisableAllowedReceipts();
// Lock id based prefs
onLockPreference("mail.identity", gIdentity.key);
// Lock server based prefs
onLockPreference("mail.server", gIncomingServer.key);
return true;
}
function EnableDisableAllowedReceipts() {
if (receiptSend) {
if (!neverReturn.getAttribute("disabled") && (receiptSend.getAttribute("value") != "false")) {
notInToCcPref.removeAttribute("disabled");
notInToCcLabel.removeAttribute("disabled");
outsideDomainPref.removeAttribute("disabled");
outsideDomainLabel.removeAttribute("disabled");
otherCasesPref.removeAttribute("disabled");
otherCasesLabel.removeAttribute("disabled");
}
else {
notInToCcPref.setAttribute("disabled", "true");
notInToCcLabel.setAttribute("disabled", "true");
outsideDomainPref.setAttribute("disabled", "true");
outsideDomainLabel.setAttribute("disabled", "true");
otherCasesPref.setAttribute("disabled", "true");
otherCasesLabel.setAttribute("disabled", "true");
}
}
return true;
}
function onPreInit(account, accountValues)
{
gIdentity = account.defaultIdentity;
gIncomingServer = account.incomingServer;
}
// Disables xul elements that have associated preferences locked.
function onLockPreference(initPrefString, keyString)
{
var finalPrefString;
var allPrefElements = [
{ prefstring:"request_return_receipt_on", id:"identity.request_return_receipt_on"},
{ prefstring:"select_custom_prefs", id:"identity.select_custom_prefs"},
{ prefstring:"select_global_prefs", id:"identity.select_global_prefs"},
{ prefstring:"incorporate_return_receipt", id:"server.incorporate_return_receipt"},
{ prefstring:"never_return", id:"never_return"},
{ prefstring:"return_some", id:"return_some"},
{ prefstring:"mdn_not_in_to_cc", id:"server.mdn_not_in_to_cc"},
{ prefstring:"mdn_outside_domain", id:"server.mdn_outside_domain"},
{ prefstring:"mdn_other", id:"server.mdn_other"},
];
finalPrefString = initPrefString + "." + keyString + ".";
gMdnPrefBranch = Services.prefs.getBranch(finalPrefString);
disableIfLocked( allPrefElements );
}
function disableIfLocked( prefstrArray )
{
for (var i=0; i<prefstrArray.length; i++) {
var id = prefstrArray[i].id;
var element = document.getElementById(id);
if (gMdnPrefBranch.prefIsLocked(prefstrArray[i].prefstring)) {
if (id == "server.incorporate_return_receipt")
{
document.getElementById("leave_in_inbox").setAttribute("disabled", "true");
document.getElementById("move_to_sent").setAttribute("disabled", "true");
}
else
element.setAttribute("disabled", "true");
}
}
}
/**
* Opens Preferences (Options) dialog on the pane and tab where
* the global receipts settings can be found.
*/
function showGlobalReceipts() {
openPrefsFromAccountManager("paneAdvanced", "generalTab",
{subdialog: "showReturnReceipts"}, "receipts_pane");
}

View file

@ -0,0 +1,136 @@
<?xml version="1.0"?>
<!--
This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://messenger/skin/accountManage.css" type="text/css"?>
<!DOCTYPE page SYSTEM "chrome://messenger/locale/am-mdn.dtd">
<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="&pane.title;"
onload="parent.onPanelLoaded('am-mdn.xul');">
<vbox flex="1" style="overflow: auto;">
<stringbundle id="bundle_smime" src="chrome://messenger/locale/am-mdn.properties"/>
<script type="application/javascript" src="chrome://messenger/content/AccountManager.js"/>
<script type="application/javascript" src="chrome://messenger/content/amUtils.js"/>
<script type="application/javascript" src="chrome://messenger/content/am-mdn.js"/>
<dialogheader title="&pane.title;"/>
<groupbox>
<caption label="&pane.title;"/>
<hbox id="prefChoices" align="center" flex="1">
<radiogroup id="identity.use_custom_prefs" wsm_persist="true" genericattr="true"
preftype="bool" prefstring="mail.identity.%identitykey%.use_custom_prefs"
oncommand="EnableDisableCustomSettings();" flex="1">
<radio id="identity.select_global_prefs"
value="false"
label="&useGlobalPrefs.label;"
accesskey="&useGlobalPrefs.accesskey;"/>
<hbox flex="1">
<spacer flex="1"/>
<button id="globalReceiptsLink"
label="&globalReceipts.label;"
accesskey="&globalReceipts.accesskey;"
oncommand="showGlobalReceipts();"/>
</hbox>
<radio id="identity.select_custom_prefs"
value="true"
label="&useCustomPrefs.label;"
accesskey="&useCustomPrefs.accesskey;"/>
</radiogroup>
</hbox>
<vbox id="returnReceiptSettings" class="indent" align="start">
<checkbox id="identity.request_return_receipt_on" label="&requestReceipt.label;"
accesskey="&requestReceipt.accesskey;"
wsm_persist="true" genericattr="true" iscontrolcontainer="true"
preftype="bool" prefstring="mail.identity.%identitykey%.request_return_receipt_on"/>
<separator/>
<vbox id="receiptArrive">
<label id="receiptArriveLabel" control="server.incorporate_return_receipt">&receiptArrive.label;</label>
<radiogroup id="server.incorporate_return_receipt" wsm_persist="true" genericattr="true"
preftype="int" prefstring="mail.server.%serverkey%.incorporate_return_receipt"
class="indent">
<radio id="leave_in_inbox" value="0" label="&leaveIt.label;"
accesskey="&leaveIt.accesskey;"/>
<radio id="move_to_sent" value="1" label="&moveToSent.label;"
accesskey="&moveToSent.accesskey;"/>
</radiogroup>
</vbox>
<separator/>
<vbox id="receiptRequest">
<label id="receiptRequestLabel" control="server.mdn_report_enabled">&requestMDN.label;</label>
<radiogroup id="server.mdn_report_enabled" wsm_persist="true" genericattr="true"
preftype="bool" prefstring="mail.server.%serverkey%.mdn_report_enabled"
oncommand="EnableDisableAllowedReceipts();"
class="indent">
<radio id="never_return" value="false" label="&never.label;"
accesskey="&never.accesskey;"/>
<radio id="return_some" value="true" label="&returnSome.label;"
accesskey="&returnSome.accesskey;"/>
<hbox id="receiptSendIf" class="indent">
<grid>
<columns><column/><column/></columns>
<rows>
<row align="center">
<label id="notInToCcLabel" value="&notInToCc.label;"
accesskey="&notInToCc.accesskey;" control="server.mdn_not_in_to_cc"/>
<menulist id="server.mdn_not_in_to_cc" wsm_persist="true" genericattr="true"
preftype="int" prefstring="mail.server.%serverkey%.mdn_not_in_to_cc">
<menupopup>
<menuitem value="0" label="&neverSend.label;"/>
<menuitem value="1" label="&alwaysSend.label;"/>
<menuitem value="2" label="&askMe.label;"/>
</menupopup>
</menulist>
</row>
<row align="center">
<label id="outsideDomainLabel" value="&outsideDomain.label;"
accesskey="&outsideDomain.accesskey;" control="server.mdn_outside_domain"/>
<menulist id="server.mdn_outside_domain" wsm_persist="true" genericattr="true"
preftype="int" prefstring="mail.server.%serverkey%.mdn_outside_domain">
<menupopup>
<menuitem value="0" label="&neverSend.label;"/>
<menuitem value="1" label="&alwaysSend.label;"/>
<menuitem value="2" label="&askMe.label;"/>
</menupopup>
</menulist>
</row>
<row align="center">
<label id="otherCasesLabel" value="&otherCases.label;"
accesskey="&otherCases.accesskey;" control="server.mdn_other"/>
<menulist id="server.mdn_other" wsm_persist="true" genericattr="true"
preftype="int" prefstring="mail.server.%serverkey%.mdn_other">
<menupopup>
<menuitem value="0" label="&neverSend.label;"/>
<menuitem value="1" label="&alwaysSend.label;"/>
<menuitem value="2" label="&askMe.label;"/>
</menupopup>
</menulist>
</row>
</rows>
</grid>
</hbox>
</radiogroup>
</vbox>
</vbox>
</groupbox>
</vbox>
</page>

View file

@ -0,0 +1,23 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* default prefs for mdn
*/
pref("mail.identity.default.use_custom_prefs", false); // false: Use global true: Use custom
pref("mail.identity.default.request_return_receipt_on", false);
pref("mail.server.default.incorporate_return_receipt", 0); // 0: Inbox/filter 1: Sent folder
pref("mail.server.default.mdn_report_enabled", true); // false: Never return receipts true: Return some receipts
pref("mail.server.default.mdn_not_in_to_cc", 2); // 0: Never 1: Always 2: Ask me 3: Denial
pref("mail.server.default.mdn_outside_domain", 2);
pref("mail.server.default.mdn_other", 2);
pref("mail.identity.default.request_receipt_header_type", 0); // return receipt header type - 0: MDN-DNT 1: RRT 2: Both
pref("mail.server.default.mdn_report_enabled", true);

View file

@ -0,0 +1,7 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
messenger.jar:
content/messenger/am-mdn.xul (content/am-mdn.xul)
content/messenger/am-mdn.js (content/am-mdn.js)

View file

@ -0,0 +1,12 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DIRS += ['src']
JAR_MANIFESTS += ['jar.mn']
JS_PREFERENCE_FILES += [
'content/mdn.js',
]

View file

@ -0,0 +1,24 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
function MDNService() {}
MDNService.prototype = {
name: "mdn",
chromePackageName: "messenger",
showPanel: function(server) {
// don't show the panel for news, rss, im or local accounts
return (server.type != "nntp" && server.type != "rss" &&
server.type != "im" && server.type != "none");
},
QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIMsgAccountManagerExtension]),
classID: Components.ID("{e007d92e-1dd1-11b2-a61e-dc962c9b8571}"),
};
var components = [MDNService];
var NSGetFactory = XPCOMUtils.generateNSGetFactory(components);

View file

@ -0,0 +1,3 @@
component {e007d92e-1dd1-11b2-a61e-dc962c9b8571} mdn-service.js
contract @mozilla.org/accountmanager/extension;1?name=mdn {e007d92e-1dd1-11b2-a61e-dc962c9b8571}
category mailnews-accountmanager-extensions mdn-account-manager-extension @mozilla.org/accountmanager/extension;1?name=mdn

View file

@ -0,0 +1,16 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
SOURCES += [
'nsMsgMdnGenerator.cpp',
]
EXTRA_COMPONENTS += [
'mdn-service.js',
'mdn-service.manifest',
]
FINAL_LIBRARY = 'mail'

View file

@ -0,0 +1,22 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsMsgMdnCID_h__
#define nsMsgMdnCID_h__
#include "nsISupports.h"
#include "nsIFactory.h"
#include "nsIComponentManager.h"
#include "nsIMsgMdnGenerator.h"
#define NS_MSGMDNGENERATOR_CONTRACTID \
"@mozilla.org/messenger-mdn/generator;1"
#define NS_MSGMDNGENERATOR_CID \
{ /* ec917b13-8f73-4d4d-9146-d7f7aafe9076 */ \
0xec917b13, 0x8f73, 0x4d4d, \
{ 0x91, 0x46, 0xd7, 0xf7, 0xaa, 0xfe, 0x90, 0x76 }}
#endif /* nsMsgMdnCID_h__ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,90 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef _nsMsgMdnGenerator_H_
#define _nsMsgMdnGenerator_H_
#include "nsIMsgMdnGenerator.h"
#include "nsCOMPtr.h"
#include "nsIUrlListener.h"
#include "nsIMsgIncomingServer.h"
#include "nsIOutputStream.h"
#include "nsIFile.h"
#include "nsIMsgIdentity.h"
#include "nsIMsgWindow.h"
#include "nsIMimeHeaders.h"
#include "nsStringGlue.h"
#include "MailNewsTypes2.h"
#define eNeverSendOp ((int32_t) 0)
#define eAutoSendOp ((int32_t) 1)
#define eAskMeOp ((int32_t) 2)
#define eDeniedOp ((int32_t) 3)
class nsMsgMdnGenerator : public nsIMsgMdnGenerator, public nsIUrlListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIMSGMDNGENERATOR
NS_DECL_NSIURLLISTENER
nsMsgMdnGenerator();
private:
virtual ~nsMsgMdnGenerator();
// Sanity Check methods
bool ProcessSendMode(); // must called prior ValidateReturnPath
bool ValidateReturnPath();
bool NotInToOrCc();
bool MailAddrMatch(const char *addr1, const char *addr2);
nsresult StoreMDNSentFlag(nsIMsgFolder *folder, nsMsgKey key);
nsresult ClearMDNNeededFlag(nsIMsgFolder *folder, nsMsgKey key);
nsresult NoteMDNRequestHandled();
nsresult CreateMdnMsg();
nsresult CreateFirstPart();
nsresult CreateSecondPart();
nsresult CreateThirdPart();
nsresult SendMdnMsg();
// string bundle helper methods
nsresult GetStringFromName(const char16_t *aName, char16_t **aResultString);
nsresult FormatStringFromName(const char16_t *aName,
const char16_t *aString,
char16_t **aResultString);
// other helper methods
nsresult InitAndProcess(bool *needToAskUser);
nsresult OutputAllHeaders();
nsresult WriteString(const char *str);
private:
EDisposeType m_disposeType;
nsCOMPtr<nsIMsgWindow> m_window;
nsCOMPtr<nsIOutputStream> m_outputStream;
nsCOMPtr<nsIFile> m_file;
nsCOMPtr<nsIMsgIdentity> m_identity;
nsMsgKey m_key;
nsCString m_charset;
nsCString m_email;
nsCString m_mimeSeparator;
nsCString m_messageId;
nsCOMPtr<nsIMsgFolder> m_folder;
nsCOMPtr<nsIMsgIncomingServer> m_server;
nsCOMPtr<nsIMimeHeaders> m_headers;
nsCString m_dntRrt;
int32_t m_notInToCcOp;
int32_t m_outsideDomainOp;
int32_t m_otherOp;
bool m_reallySendMdn;
bool m_autoSend;
bool m_autoAction;
bool m_mdnEnabled;
};
#endif // _nsMsgMdnGenerator_H_

View file

@ -0,0 +1,19 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# These extensions are not optional.
DIRS += [
'mdn',
'mailviews/public',
'mailviews/src',
'mailviews/content',
'bayesian-spam-filter',
'offline-startup',
'newsblog',
'fts3/public',
'fts3/src',
'smime',
]

View file

@ -0,0 +1,620 @@
/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Cache for all of the feeds currently being downloaded, indexed by URL,
// so the load event listener can access the Feed objects after it finishes
// downloading the feed.
var FeedCache =
{
mFeeds: {},
putFeed: function (aFeed)
{
this.mFeeds[this.normalizeHost(aFeed.url)] = aFeed;
},
getFeed: function (aUrl)
{
let index = this.normalizeHost(aUrl);
if (index in this.mFeeds)
return this.mFeeds[index];
return null;
},
removeFeed: function (aUrl)
{
let index = this.normalizeHost(aUrl);
if (index in this.mFeeds)
delete this.mFeeds[index];
},
normalizeHost: function (aUrl)
{
try
{
let normalizedUrl = Services.io.newURI(aUrl, null, null);
normalizedUrl.host = normalizedUrl.host.toLowerCase();
return normalizedUrl.spec
}
catch (ex)
{
return aUrl;
}
}
};
function Feed(aResource, aRSSServer)
{
this.resource = aResource.QueryInterface(Ci.nsIRDFResource);
this.server = aRSSServer;
}
Feed.prototype =
{
description: null,
author: null,
request: null,
server: null,
downloadCallback: null,
resource: null,
items: new Array(),
itemsStored: 0,
mFolder: null,
mInvalidFeed: false,
mFeedType: null,
mLastModified: null,
get folder()
{
return this.mFolder;
},
set folder (aFolder)
{
this.mFolder = aFolder;
},
get name()
{
// Used for the feed's title in Subcribe dialog and opml export.
let name = this.title || this.description || this.url;
return name.replace(/[\n\r\t]+/g, " ").replace(/[\x00-\x1F]+/g, "");
},
get folderName()
{
if (this.mFolderName)
return this.mFolderName;
// Get a unique sanitized name. Use title or description as a base;
// these are mandatory by spec. Length of 80 is plenty.
let folderName = (this.title || this.description || "").substr(0,80);
let defaultName = FeedUtils.strings.GetStringFromName("ImportFeedsNew");
return this.mFolderName = FeedUtils.getSanitizedFolderName(this.server.rootMsgFolder,
folderName,
defaultName,
true);
},
download: function(aParseItems, aCallback)
{
// May be null.
this.downloadCallback = aCallback;
// Whether or not to parse items when downloading and parsing the feed.
// Defaults to true, but setting to false is useful for obtaining
// just the title of the feed when the user subscribes to it.
this.parseItems = aParseItems == null ? true : aParseItems ? true : false;
// Before we do anything, make sure the url is an http url. This is just
// a sanity check so we don't try opening mailto urls, imap urls, etc. that
// the user may have tried to subscribe to as an rss feed.
if (!FeedUtils.isValidScheme(this.url))
{
// Simulate an invalid feed error.
FeedUtils.log.info("Feed.download: invalid protocol for - " + this.url);
this.onParseError(this);
return;
}
// Before we try to download the feed, make sure we aren't already
// processing the feed by looking up the url in our feed cache.
if (FeedCache.getFeed(this.url))
{
if (this.downloadCallback)
this.downloadCallback.downloaded(this, FeedUtils.kNewsBlogFeedIsBusy);
// Return, the feed is already in use.
return;
}
if (Services.io.offline) {
// If offline and don't want to go online, just add the feed subscription;
// it can be verified later (the folder name will be the url if not adding
// to an existing folder). Only for subscribe actions; passive biff and
// active get new messages are handled prior to getting here.
let win = Services.wm.getMostRecentWindow("mail:3pane");
if (!win.MailOfflineMgr.getNewMail()) {
this.storeNextItem();
return;
}
}
this.request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
createInstance(Ci.nsIXMLHttpRequest);
// Must set onProgress before calling open.
this.request.onprogress = this.onProgress;
this.request.open("GET", this.url, true);
this.request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE |
Ci.nsIRequest.INHIBIT_CACHING;
// Some servers, if sent If-Modified-Since, will send 304 if subsequently
// not sent If-Modified-Since, as in the case of an unsubscribe and new
// subscribe. Send start of century date to force a download; some servers
// will 304 on older dates (such as epoch 1970).
let lastModified = this.lastModified || "Sat, 01 Jan 2000 00:00:00 GMT";
this.request.setRequestHeader("If-Modified-Since", lastModified);
// Only order what you're going to eat...
this.request.responseType = "document";
this.request.overrideMimeType("text/xml");
this.request.setRequestHeader("Accept", FeedUtils.REQUEST_ACCEPT);
this.request.timeout = FeedUtils.REQUEST_TIMEOUT;
this.request.onload = this.onDownloaded;
this.request.onerror = this.onDownloadError;
this.request.ontimeout = this.onDownloadError;
FeedCache.putFeed(this);
this.request.send(null);
},
onDownloaded: function(aEvent)
{
let request = aEvent.target;
let isHttp = request.channel.originalURI.scheme.startsWith("http");
let url = request.channel.originalURI.spec;
if (isHttp && (request.status < 200 || request.status >= 300))
{
Feed.prototype.onDownloadError(aEvent);
return;
}
FeedUtils.log.debug("Feed.onDownloaded: got a download - " + url);
let feed = FeedCache.getFeed(url);
if (!feed)
throw new Error("Feed.onDownloaded: error - couldn't retrieve feed " +
"from cache");
// If the server sends a Last-Modified header, store the property on the
// feed so we can use it when making future requests, to avoid downloading
// and parsing feeds that have not changed. Don't update if merely checking
// the url, as for subscribe move/copy, as a subsequent refresh may get a 304.
// Save the response and persist it only upon successful completion of the
// refresh cycle (i.e. not if the request is cancelled).
let lastModifiedHeader = request.getResponseHeader("Last-Modified");
feed.mLastModified = (lastModifiedHeader && feed.parseItems) ?
lastModifiedHeader : null;
// The download callback is called asynchronously when parse() is done.
feed.parse();
},
onProgress: function(aEvent)
{
let request = aEvent.target;
let url = request.channel.originalURI.spec;
let feed = FeedCache.getFeed(url);
if (feed.downloadCallback)
feed.downloadCallback.onProgress(feed, aEvent.loaded, aEvent.total,
aEvent.lengthComputable);
},
onDownloadError: function(aEvent)
{
let request = aEvent.target;
let url = request.channel.originalURI.spec;
let feed = FeedCache.getFeed(url);
if (feed.downloadCallback)
{
// Generic network or 'not found' error initially.
let error = FeedUtils.kNewsBlogRequestFailure;
if (request.status == 304) {
// If the http status code is 304, the feed has not been modified
// since we last downloaded it and does not need to be parsed.
error = FeedUtils.kNewsBlogNoNewItems;
}
else {
let [errType, errName] = FeedUtils.createTCPErrorFromFailedXHR(request);
FeedUtils.log.info("Feed.onDownloaded: request errType:errName:statusCode - " +
errType + ":" + errName + ":" + request.status);
if (errType == "SecurityCertificate")
// This is the code for nsINSSErrorsService.ERROR_CLASS_BAD_CERT
// overrideable security certificate errors.
error = FeedUtils.kNewsBlogBadCertError;
if (request.status == 401 || request.status == 403)
// Unauthorized or Forbidden.
error = FeedUtils.kNewsBlogNoAuthError;
}
feed.downloadCallback.downloaded(feed, error);
}
FeedCache.removeFeed(url);
},
onParseError: function(aFeed)
{
if (!aFeed)
return;
aFeed.mInvalidFeed = true;
if (aFeed.downloadCallback)
aFeed.downloadCallback.downloaded(aFeed, FeedUtils.kNewsBlogInvalidFeed);
FeedCache.removeFeed(aFeed.url);
},
onUrlChange: function(aFeed, aOldUrl)
{
if (!aFeed)
return;
// Simulate a cancel after a url update; next cycle will check the new url.
aFeed.mInvalidFeed = true;
if (aFeed.downloadCallback)
aFeed.downloadCallback.downloaded(aFeed, FeedUtils.kNewsBlogCancel);
FeedCache.removeFeed(aOldUrl);
},
get url()
{
let ds = FeedUtils.getSubscriptionsDS(this.server);
let url = ds.GetTarget(this.resource, FeedUtils.DC_IDENTIFIER, true);
if (url)
url = url.QueryInterface(Ci.nsIRDFLiteral).Value;
else
url = this.resource.ValueUTF8;
return url;
},
get title()
{
let ds = FeedUtils.getSubscriptionsDS(this.server);
let title = ds.GetTarget(this.resource, FeedUtils.DC_TITLE, true);
if (title)
title = title.QueryInterface(Ci.nsIRDFLiteral).Value;
return title;
},
set title (aNewTitle)
{
if (!aNewTitle)
return;
let ds = FeedUtils.getSubscriptionsDS(this.server);
aNewTitle = FeedUtils.rdf.GetLiteral(aNewTitle);
let old_title = ds.GetTarget(this.resource, FeedUtils.DC_TITLE, true);
if (old_title)
ds.Change(this.resource, FeedUtils.DC_TITLE, old_title, aNewTitle);
else
ds.Assert(this.resource, FeedUtils.DC_TITLE, aNewTitle, true);
},
get lastModified()
{
let ds = FeedUtils.getSubscriptionsDS(this.server);
let lastModified = ds.GetTarget(this.resource,
FeedUtils.DC_LASTMODIFIED,
true);
if (lastModified)
lastModified = lastModified.QueryInterface(Ci.nsIRDFLiteral).Value;
return lastModified;
},
set lastModified(aLastModified)
{
let ds = FeedUtils.getSubscriptionsDS(this.server);
aLastModified = FeedUtils.rdf.GetLiteral(aLastModified);
let old_lastmodified = ds.GetTarget(this.resource,
FeedUtils.DC_LASTMODIFIED,
true);
if (old_lastmodified)
ds.Change(this.resource, FeedUtils.DC_LASTMODIFIED,
old_lastmodified, aLastModified);
else
ds.Assert(this.resource, FeedUtils.DC_LASTMODIFIED, aLastModified, true);
},
get quickMode ()
{
let ds = FeedUtils.getSubscriptionsDS(this.server);
let quickMode = ds.GetTarget(this.resource, FeedUtils.FZ_QUICKMODE, true);
if (quickMode)
{
quickMode = quickMode.QueryInterface(Ci.nsIRDFLiteral);
quickMode = quickMode.Value == "true";
}
return quickMode;
},
set quickMode (aNewQuickMode)
{
let ds = FeedUtils.getSubscriptionsDS(this.server);
aNewQuickMode = FeedUtils.rdf.GetLiteral(aNewQuickMode);
let old_quickMode = ds.GetTarget(this.resource,
FeedUtils.FZ_QUICKMODE,
true);
if (old_quickMode)
ds.Change(this.resource, FeedUtils.FZ_QUICKMODE,
old_quickMode, aNewQuickMode);
else
ds.Assert(this.resource, FeedUtils.FZ_QUICKMODE,
aNewQuickMode, true);
},
get options ()
{
let ds = FeedUtils.getSubscriptionsDS(this.server);
let options = ds.GetTarget(this.resource, FeedUtils.FZ_OPTIONS, true);
if (options)
return JSON.parse(options.QueryInterface(Ci.nsIRDFLiteral).Value);
return null;
},
set options (aOptions)
{
let newOptions = aOptions ? FeedUtils.newOptions(aOptions) :
FeedUtils._optionsDefault;
let ds = FeedUtils.getSubscriptionsDS(this.server);
newOptions = FeedUtils.rdf.GetLiteral(JSON.stringify(newOptions));
let oldOptions = ds.GetTarget(this.resource, FeedUtils.FZ_OPTIONS, true);
if (oldOptions)
ds.Change(this.resource, FeedUtils.FZ_OPTIONS, oldOptions, newOptions);
else
ds.Assert(this.resource, FeedUtils.FZ_OPTIONS, newOptions, true);
},
categoryPrefs: function ()
{
let categoryPrefsAcct = FeedUtils.getOptionsAcct(this.server).category;
if (!this.options)
return categoryPrefsAcct;
return this.options.category;
},
get link ()
{
let ds = FeedUtils.getSubscriptionsDS(this.server);
let link = ds.GetTarget(this.resource, FeedUtils.RSS_LINK, true);
if (link)
link = link.QueryInterface(Ci.nsIRDFLiteral).Value;
return link;
},
set link (aNewLink)
{
if (!aNewLink)
return;
let ds = FeedUtils.getSubscriptionsDS(this.server);
aNewLink = FeedUtils.rdf.GetLiteral(aNewLink);
let old_link = ds.GetTarget(this.resource, FeedUtils.RSS_LINK, true);
if (old_link)
ds.Change(this.resource, FeedUtils.RSS_LINK, old_link, aNewLink);
else
ds.Assert(this.resource, FeedUtils.RSS_LINK, aNewLink, true);
},
parse: function()
{
// Create a feed parser which will parse the feed.
let parser = new FeedParser();
this.itemsToStore = parser.parseFeed(this, this.request.responseXML);
parser = null;
if (this.mInvalidFeed)
{
this.request = null;
this.mInvalidFeed = false;
return;
}
// storeNextItem() will iterate through the parsed items, storing each one.
this.itemsToStoreIndex = 0;
this.itemsStored = 0;
this.storeNextItem();
},
invalidateItems: function ()
{
let ds = FeedUtils.getItemsDS(this.server);
FeedUtils.log.debug("Feed.invalidateItems: for url - " + this.url);
let items = ds.GetSources(FeedUtils.FZ_FEED, this.resource, true);
let item;
while (items.hasMoreElements())
{
item = items.getNext();
item = item.QueryInterface(Ci.nsIRDFResource);
FeedUtils.log.trace("Feed.invalidateItems: item - " + item.Value);
let valid = ds.GetTarget(item, FeedUtils.FZ_VALID, true);
if (valid)
ds.Unassert(item, FeedUtils.FZ_VALID, valid, true);
}
},
removeInvalidItems: function(aDeleteFeed)
{
let ds = FeedUtils.getItemsDS(this.server);
FeedUtils.log.debug("Feed.removeInvalidItems: for url - " + this.url);
let items = ds.GetSources(FeedUtils.FZ_FEED, this.resource, true);
let item;
let currentTime = new Date().getTime();
while (items.hasMoreElements())
{
item = items.getNext();
item = item.QueryInterface(Ci.nsIRDFResource);
if (ds.HasAssertion(item, FeedUtils.FZ_VALID,
FeedUtils.RDF_LITERAL_TRUE, true))
continue;
let lastSeenTime = ds.GetTarget(item, FeedUtils.FZ_LAST_SEEN_TIMESTAMP, true);
if (lastSeenTime)
lastSeenTime = parseInt(lastSeenTime.QueryInterface(Ci.nsIRDFLiteral).Value)
else
lastSeenTime = 0;
if ((currentTime - lastSeenTime) < FeedUtils.INVALID_ITEM_PURGE_DELAY &&
!aDeleteFeed)
// Don't immediately purge items in active feeds; do so for deleted feeds.
continue;
FeedUtils.log.trace("Feed.removeInvalidItems: item - " + item.Value);
ds.Unassert(item, FeedUtils.FZ_FEED, this.resource, true);
if (ds.hasArcOut(item, FeedUtils.FZ_FEED))
FeedUtils.log.debug("Feed.removeInvalidItems: " + item.Value +
" is from more than one feed; only the reference to" +
" this feed removed");
else
FeedUtils.removeAssertions(ds, item);
}
},
createFolder: function()
{
if (this.folder)
return;
try {
this.folder = this.server.rootMsgFolder
.QueryInterface(Ci.nsIMsgLocalMailFolder)
.createLocalSubfolder(this.folderName);
}
catch (ex) {
// An error creating.
FeedUtils.log.info("Feed.createFolder: error creating folder - '"+
this.folderName+"' in parent folder "+
this.server.rootMsgFolder.filePath.path + " -- "+ex);
// But its remnants are still there, clean up.
let xfolder = this.server.rootMsgFolder.getChildNamed(this.folderName);
this.server.rootMsgFolder.propagateDelete(xfolder, true, null);
}
},
// Gets the next item from itemsToStore and forces that item to be stored
// to the folder. If more items are left to be stored, fires a timer for
// the next one, otherwise triggers a download done notification to the UI.
storeNextItem: function()
{
if (FeedUtils.CANCEL_REQUESTED)
{
FeedUtils.CANCEL_REQUESTED = false;
this.cleanupParsingState(this, FeedUtils.kNewsBlogCancel);
return;
}
if (!this.itemsToStore || !this.itemsToStore.length)
{
let code = FeedUtils.kNewsBlogSuccess;
this.createFolder();
if (!this.folder)
code = FeedUtils.kNewsBlogFileError;
this.cleanupParsingState(this, code);
return;
}
let item = this.itemsToStore[this.itemsToStoreIndex];
if (item.store())
this.itemsStored++;
if (!this.folder)
{
this.cleanupParsingState(this, FeedUtils.kNewsBlogFileError);
return;
}
this.itemsToStoreIndex++;
// If the listener is tracking progress for each item, report it here.
if (item.feed.downloadCallback && item.feed.downloadCallback.onFeedItemStored)
item.feed.downloadCallback.onFeedItemStored(item.feed,
this.itemsToStoreIndex,
this.itemsToStore.length);
// Eventually we'll report individual progress here.
if (this.itemsToStoreIndex < this.itemsToStore.length)
{
if (!this.storeItemsTimer)
this.storeItemsTimer = Cc["@mozilla.org/timer;1"].
createInstance(Ci.nsITimer);
this.storeItemsTimer.initWithCallback(this, 50, Ci.nsITimer.TYPE_ONE_SHOT);
}
else
{
// We have just finished downloading one or more feed items into the
// destination folder; if the folder is still listed as having new
// messages in it, then we should set the biff state on the folder so the
// right RDF UI changes happen in the folder pane to indicate new mail.
if (item.feed.folder.hasNewMessages)
{
item.feed.folder.biffState = Ci.nsIMsgFolder.nsMsgBiffState_NewMail;
// Run the bayesian spam filter, if enabled.
item.feed.folder.callFilterPlugins(null);
}
this.cleanupParsingState(this, FeedUtils.kNewsBlogSuccess);
}
},
cleanupParsingState: function(aFeed, aCode)
{
// Now that we are done parsing the feed, remove the feed from the cache.
FeedCache.removeFeed(aFeed.url);
if (aFeed.parseItems)
{
// Do this only if we're in parse/store mode.
aFeed.removeInvalidItems(false);
if (aCode == FeedUtils.kNewsBlogSuccess && aFeed.mLastModified)
aFeed.lastModified = aFeed.mLastModified;
// Flush any feed item changes to disk.
let ds = FeedUtils.getItemsDS(aFeed.server);
ds.Flush();
FeedUtils.log.debug("Feed.cleanupParsingState: items stored - " + this.itemsStored);
}
// Force the xml http request to go away. This helps reduce some nasty
// assertions on shut down.
this.request = null;
this.itemsToStore = "";
this.itemsToStoreIndex = 0;
this.itemsStored = 0;
this.storeItemsTimer = null;
if (aFeed.downloadCallback)
aFeed.downloadCallback.downloaded(aFeed, aCode);
},
// nsITimerCallback
notify: function(aTimer)
{
this.storeNextItem();
}
};

View file

@ -0,0 +1,490 @@
/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
function FeedItem()
{
this.mDate = FeedUtils.getValidRFC5322Date();
this.mUnicodeConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
createInstance(Ci.nsIScriptableUnicodeConverter);
this.mParserUtils = Cc["@mozilla.org/parserutils;1"].
getService(Ci.nsIParserUtils);
}
FeedItem.prototype =
{
// Only for IETF Atom.
xmlContentBase: null,
id: null,
feed: null,
description: null,
content: null,
enclosures: [],
title: null,
author: "anonymous",
inReplyTo: "",
keywords: [],
mURL: null,
characterSet: "UTF-8",
ENCLOSURE_BOUNDARY_PREFIX: "--------------", // 14 dashes
ENCLOSURE_HEADER_BOUNDARY_PREFIX: "------------", // 12 dashes
MESSAGE_TEMPLATE: '\n' +
'<html>\n' +
' <head>\n' +
' <title>%TITLE%</title>\n' +
' <base href="%BASE%">\n' +
' </head>\n' +
' <body id="msgFeedSummaryBody" selected="false">\n' +
' %CONTENT%\n' +
' </body>\n' +
'</html>\n',
get url()
{
return this.mURL;
},
set url(aVal)
{
try
{
this.mURL = Services.io.newURI(aVal, null, null).spec;
}
catch(ex)
{
// The url as published or constructed can be a non url. It's used as a
// feeditem identifier in feeditems.rdf, as a messageId, and as an href
// and for the content-base header. Save as is; ensure not null.
this.mURL = aVal ? aVal : "";
}
},
get date()
{
return this.mDate;
},
set date (aVal)
{
this.mDate = aVal;
},
get identity ()
{
return this.feed.name + ": " + this.title + " (" + this.id + ")"
},
normalizeMessageID: function(messageID)
{
// Escape occurrences of message ID meta characters <, >, and @.
messageID.replace(/</g, "%3C");
messageID.replace(/>/g, "%3E");
messageID.replace(/@/g, "%40");
messageID = "<" + messageID.trim() + "@" + "localhost.localdomain" + ">";
FeedUtils.log.trace("FeedItem.normalizeMessageID: messageID - " + messageID);
return messageID;
},
get itemUniqueURI()
{
return this.createURN(this.id);
},
get contentBase()
{
if(this.xmlContentBase)
return this.xmlContentBase
else
return this.mURL;
},
store: function()
{
// this.title and this.content contain HTML.
// this.mUrl and this.contentBase contain plain text.
let stored = false;
let resource = this.findStoredResource();
if (!this.feed.folder)
return stored;
if (resource == null)
{
resource = FeedUtils.rdf.GetResource(this.itemUniqueURI);
if (!this.content)
{
FeedUtils.log.trace("FeedItem.store: " + this.identity +
" no content; storing description or title");
this.content = this.description || this.title;
}
let content = this.MESSAGE_TEMPLATE;
content = content.replace(/%TITLE%/, this.title);
content = content.replace(/%BASE%/, this.htmlEscape(this.contentBase));
content = content.replace(/%CONTENT%/, this.content);
this.content = content;
this.writeToFolder();
this.markStored(resource);
stored = true;
}
this.markValid(resource);
return stored;
},
findStoredResource: function()
{
// Checks to see if the item has already been stored in its feed's
// message folder.
FeedUtils.log.trace("FeedItem.findStoredResource: checking if stored - " +
this.identity);
let server = this.feed.server;
let folder = this.feed.folder;
if (!folder)
{
FeedUtils.log.debug("FeedItem.findStoredResource: folder '" +
this.feed.folderName +
"' doesn't exist; creating as child of " +
server.rootMsgFolder.prettyName + "\n");
this.feed.createFolder();
return null;
}
let ds = FeedUtils.getItemsDS(server);
let itemURI = this.itemUniqueURI;
let itemResource = FeedUtils.rdf.GetResource(itemURI);
let downloaded = ds.GetTarget(itemResource, FeedUtils.FZ_STORED, true);
if (!downloaded ||
downloaded.QueryInterface(Ci.nsIRDFLiteral).Value == "false")
{
FeedUtils.log.trace("FeedItem.findStoredResource: not stored");
return null;
}
FeedUtils.log.trace("FeedItem.findStoredResource: already stored");
return itemResource;
},
markValid: function(resource)
{
let ds = FeedUtils.getItemsDS(this.feed.server);
let newTimeStamp = FeedUtils.rdf.GetLiteral(new Date().getTime());
let currentTimeStamp = ds.GetTarget(resource,
FeedUtils.FZ_LAST_SEEN_TIMESTAMP,
true);
if (currentTimeStamp)
ds.Change(resource, FeedUtils.FZ_LAST_SEEN_TIMESTAMP,
currentTimeStamp, newTimeStamp);
else
ds.Assert(resource, FeedUtils.FZ_LAST_SEEN_TIMESTAMP,
newTimeStamp, true);
if (!ds.HasAssertion(resource, FeedUtils.FZ_FEED,
FeedUtils.rdf.GetResource(this.feed.url), true))
ds.Assert(resource, FeedUtils.FZ_FEED,
FeedUtils.rdf.GetResource(this.feed.url), true);
if (ds.hasArcOut(resource, FeedUtils.FZ_VALID))
{
let currentValue = ds.GetTarget(resource, FeedUtils.FZ_VALID, true);
ds.Change(resource, FeedUtils.FZ_VALID,
currentValue, FeedUtils.RDF_LITERAL_TRUE);
}
else
ds.Assert(resource, FeedUtils.FZ_VALID, FeedUtils.RDF_LITERAL_TRUE, true);
},
markStored: function(resource)
{
let ds = FeedUtils.getItemsDS(this.feed.server);
if (!ds.HasAssertion(resource, FeedUtils.FZ_FEED,
FeedUtils.rdf.GetResource(this.feed.url), true))
ds.Assert(resource, FeedUtils.FZ_FEED,
FeedUtils.rdf.GetResource(this.feed.url), true);
let currentValue;
if (ds.hasArcOut(resource, FeedUtils.FZ_STORED))
{
currentValue = ds.GetTarget(resource, FeedUtils.FZ_STORED, true);
ds.Change(resource, FeedUtils.FZ_STORED,
currentValue, FeedUtils.RDF_LITERAL_TRUE);
}
else
ds.Assert(resource, FeedUtils.FZ_STORED,
FeedUtils.RDF_LITERAL_TRUE, true);
},
mimeEncodeSubject: function(aSubject, aCharset)
{
// This routine sometimes throws exceptions for mis-encoded data so
// wrap it with a try catch for now.
let newSubject;
try
{
newSubject = mailServices.mimeConverter.encodeMimePartIIStr_UTF8(aSubject,
false,
aCharset, 9, 72);
}
catch (ex)
{
newSubject = aSubject;
}
return newSubject;
},
writeToFolder: function()
{
FeedUtils.log.trace("FeedItem.writeToFolder: " + this.identity +
" writing to message folder " + this.feed.name);
// Convert the title to UTF-16 before performing our HTML entity
// replacement reg expressions.
let title = this.title;
// The subject may contain HTML entities. Convert these to their unencoded
// state. i.e. &amp; becomes '&'.
title = this.mParserUtils.convertToPlainText(
title,
Ci.nsIDocumentEncoder.OutputSelectionOnly |
Ci.nsIDocumentEncoder.OutputAbsoluteLinks,
0);
// Compress white space in the subject to make it look better. Trim
// leading/trailing spaces to prevent mbox header folding issue at just
// the right subject length.
title = title.replace(/[\t\r\n]+/g, " ").trim();
this.title = this.mimeEncodeSubject(title, this.characterSet);
// If the date looks like it's in W3C-DTF format, convert it into
// an IETF standard date. Otherwise assume it's in IETF format.
if (this.mDate.search(/^\d\d\d\d/) != -1)
this.mDate = new Date(this.mDate).toUTCString();
// If there is an inreplyto value, create the headers.
let inreplytoHdrsStr = this.inReplyTo ?
("References: " + this.inReplyTo + "\n" +
"In-Reply-To: " + this.inReplyTo + "\n") : "";
// If there are keywords (categories), create the headers. In the case of
// a longer than RFC5322 recommended line length, create multiple folded
// lines (easier to parse than multiple Keywords headers).
let keywordsStr = "";
if (this.keywords.length)
{
let HEADER = "Keywords: ";
let MAXLEN = 78;
keywordsStr = HEADER;
let keyword;
let keywords = [].concat(this.keywords);
let lines = [];
while (keywords.length)
{
keyword = keywords.shift();
if (keywordsStr.length + keyword.length > MAXLEN)
{
lines.push(keywordsStr)
keywordsStr = " ".repeat(HEADER.length);
}
keywordsStr += keyword + ",";
}
keywordsStr = keywordsStr.replace(/,$/,"\n");
lines.push(keywordsStr)
keywordsStr = lines.join("\n");
}
// Escape occurrences of "From " at the beginning of lines of
// content per the mbox standard, since "From " denotes a new
// message, and add a line break so we know the last line has one.
this.content = this.content.replace(/([\r\n]+)(>*From )/g, "$1>$2");
this.content += "\n";
// The opening line of the message, mandated by standards to start
// with "From ". It's useful to construct this separately because
// we not only need to write it into the message, we also need to
// use it to calculate the offset of the X-Mozilla-Status lines from
// the front of the message for the statusOffset property of the
// DB header object.
let openingLine = 'From - ' + this.mDate + '\n';
let source =
openingLine +
'X-Mozilla-Status: 0000\n' +
'X-Mozilla-Status2: 00000000\n' +
'X-Mozilla-Keys: ' + " ".repeat(80) + '\n' +
'Received: by localhost; ' + FeedUtils.getValidRFC5322Date() + '\n' +
'Date: ' + this.mDate + '\n' +
'Message-Id: ' + this.normalizeMessageID(this.id) + '\n' +
'From: ' + this.author + '\n' +
'MIME-Version: 1.0\n' +
'Subject: ' + this.title + '\n' +
inreplytoHdrsStr +
keywordsStr +
'Content-Transfer-Encoding: 8bit\n' +
'Content-Base: ' + this.mURL + '\n';
if (this.enclosures.length)
{
let boundaryID = source.length;
source += 'Content-Type: multipart/mixed; boundary="' +
this.ENCLOSURE_HEADER_BOUNDARY_PREFIX + boundaryID + '"' + '\n\n' +
'This is a multi-part message in MIME format.\n' +
this.ENCLOSURE_BOUNDARY_PREFIX + boundaryID + '\n' +
'Content-Type: text/html; charset=' + this.characterSet + '\n' +
'Content-Transfer-Encoding: 8bit\n' +
this.content;
this.enclosures.forEach(function(enclosure) {
source += enclosure.convertToAttachment(boundaryID);
});
source += this.ENCLOSURE_BOUNDARY_PREFIX + boundaryID + '--' + '\n\n\n';
}
else
source += 'Content-Type: text/html; charset=' + this.characterSet + '\n' +
this.content;
FeedUtils.log.trace("FeedItem.writeToFolder: " + this.identity +
" is " + source.length + " characters long");
// Get the folder and database storing the feed's messages and headers.
let folder = this.feed.folder.QueryInterface(Ci.nsIMsgLocalMailFolder);
let msgFolder = folder.QueryInterface(Ci.nsIMsgFolder);
msgFolder.gettingNewMessages = true;
// Source is a unicode string, we want to save a char * string in
// the original charset. So convert back.
this.mUnicodeConverter.charset = this.characterSet;
let msgDBHdr = folder.addMessage(this.mUnicodeConverter.ConvertFromUnicode(source));
msgDBHdr.OrFlags(Ci.nsMsgMessageFlags.FeedMsg);
msgFolder.gettingNewMessages = false;
this.tagItem(msgDBHdr, this.keywords);
},
/**
* Autotag messages.
*
* @param nsIMsgDBHdr aMsgDBHdr - message to tag
* @param array aKeywords - keywords (tags)
*/
tagItem: function(aMsgDBHdr, aKeywords)
{
let categoryPrefs = this.feed.categoryPrefs();
if (!aKeywords.length || !categoryPrefs.enabled)
return;
let msgArray = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
msgArray.appendElement(aMsgDBHdr, false);
let prefix = categoryPrefs.prefixEnabled ? categoryPrefs.prefix : "";
let rtl = Services.prefs.getIntPref("bidi.direction") == 2;
let keys = [];
for (let keyword of aKeywords)
{
keyword = rtl ? keyword + prefix : prefix + keyword;
let keyForTag = MailServices.tags.getKeyForTag(keyword);
if (!keyForTag)
{
// Add the tag if it doesn't exist.
MailServices.tags.addTag(keyword, "", FeedUtils.AUTOTAG);
keyForTag = MailServices.tags.getKeyForTag(keyword);
}
// Add the tag key to the keys array.
keys.push(keyForTag);
}
if (keys.length)
// Add the keys to the message.
aMsgDBHdr.folder.addKeywordsToMessages(msgArray, keys.join(" "));
},
htmlEscape: function(s)
{
s = s.replace(/&/g, "&amp;");
s = s.replace(/>/g, "&gt;");
s = s.replace(/</g, "&lt;");
s = s.replace(/'/g, "&#39;");
s = s.replace(/"/g, "&quot;");
return s;
},
createURN: function(aName)
{
// Returns name as a URN in the 'feeditem' namespace. The returned URN is
// (or is intended to be) RFC2141 compliant.
// The builtin encodeURI provides nearly the exact encoding functionality
// required by the RFC. The exceptions are that NULL characters should not
// appear, and that #, /, ?, &, and ~ should be escaped.
// NULL characters are removed before encoding.
let name = aName.replace(/\0/g, "");
let encoded = encodeURI(name);
encoded = encoded.replace(/\#/g, "%23");
encoded = encoded.replace(/\//g, "%2f");
encoded = encoded.replace(/\?/g, "%3f");
encoded = encoded.replace(/\&/g, "%26");
encoded = encoded.replace(/\~/g, "%7e");
return FeedUtils.FZ_ITEM_NS + encoded;
}
};
// A feed enclosure is to RSS what an attachment is for e-mail. We make
// enclosures look like attachments in the UI.
function FeedEnclosure(aURL, aContentType, aLength, aTitle)
{
this.mURL = aURL;
// Store a reasonable mimetype if content-type is not present.
this.mContentType = aContentType || "application/unknown";
this.mLength = aLength;
this.mTitle = aTitle;
// Generate a fileName from the URL.
if (this.mURL)
{
try
{
this.mFileName = Services.io.newURI(this.mURL, null, null).
QueryInterface(Ci.nsIURL).
fileName;
}
catch(ex)
{
this.mFileName = this.mURL;
}
}
}
FeedEnclosure.prototype =
{
mURL: "",
mContentType: "",
mLength: 0,
mFileName: "",
mTitle: "",
ENCLOSURE_BOUNDARY_PREFIX: "--------------", // 14 dashes
// Returns a string that looks like an e-mail attachment which represents
// the enclosure.
convertToAttachment: function(aBoundaryID)
{
return '\n' +
this.ENCLOSURE_BOUNDARY_PREFIX + aBoundaryID + '\n' +
'Content-Type: ' + this.mContentType +
'; name="' + (this.mTitle || this.mFileName) +
(this.mLength ? '"; size=' + this.mLength : '"') + '\n' +
'X-Mozilla-External-Attachment-URL: ' + this.mURL + '\n' +
'Content-Disposition: attachment; filename="' + this.mFileName + '"\n\n' +
FeedUtils.strings.GetStringFromName("externalAttachmentMsg") + '\n';
}
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,63 @@
/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Components.utils.import("resource:///modules/FeedUtils.jsm");
var gServer, autotagEnable, autotagUsePrefix, autotagPrefix;
function onInit(aPageId, aServerId)
{
var accountName = document.getElementById("server.prettyName");
var title = document.getElementById("am-newsblog-title");
var defaultTitle = title.getAttribute("defaultTitle");
var titleValue;
if (accountName.value)
titleValue = defaultTitle + " - <" + accountName.value + ">";
else
titleValue = defaultTitle;
title.setAttribute("title", titleValue);
document.title = titleValue;
onCheckItem("server.biffMinutes", ["server.doBiff"]);
autotagEnable = document.getElementById("autotagEnable");
autotagUsePrefix = document.getElementById("autotagUsePrefix");
autotagPrefix = document.getElementById("autotagPrefix");
let categoryPrefsAcct = FeedUtils.getOptionsAcct(gServer).category;
autotagEnable.checked = categoryPrefsAcct.enabled;
autotagUsePrefix.disabled = !autotagEnable.checked;
autotagUsePrefix.checked = categoryPrefsAcct.prefixEnabled;
autotagPrefix.disabled = autotagUsePrefix.disabled || !autotagUsePrefix.checked;
autotagPrefix.value = categoryPrefsAcct.prefix;
}
function onPreInit(account, accountValues)
{
gServer = account.incomingServer;
}
function setCategoryPrefs(aNode)
{
let options = FeedUtils.getOptionsAcct(gServer);
switch (aNode.id) {
case "autotagEnable":
options.category.enabled = aNode.checked;
autotagUsePrefix.disabled = !aNode.checked;
autotagPrefix.disabled = !aNode.checked || !autotagUsePrefix.checked;
break;
case "autotagUsePrefix":
options.category.prefixEnabled = aNode.checked;
autotagPrefix.disabled = aNode.disabled || !aNode.checked;
break;
case "autotagPrefix":
options.category.prefix = aNode.value;
break;
}
FeedUtils.setOptionsAcct(gServer, options)
}

View file

@ -0,0 +1,155 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://messenger/skin/accountManage.css" type="text/css"?>
<?xml-stylesheet href="chrome://messenger-newsblog/skin/feed-subscriptions.css" type="text/css"?>
<!DOCTYPE page [
<!ENTITY % newsblogDTD SYSTEM "chrome://messenger-newsblog/locale/am-newsblog.dtd" >
%newsblogDTD;
<!ENTITY % feedDTD SYSTEM "chrome://messenger-newsblog/locale/feed-subscriptions.dtd" >
%feedDTD;
<!ENTITY % accountNoIdentDTD SYSTEM "chrome://messenger/locale/am-serverwithnoidentities.dtd" >
%accountNoIdentDTD;
<!ENTITY % accountServerTopDTD SYSTEM "chrome://messenger/locale/am-server-top.dtd">
%accountServerTopDTD;
]>
<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
class="color-dialog"
title="&accountTitle.label;"
onload="parent.onPanelLoaded('am-newsblog.xul');">
<script type="application/javascript"
src="chrome://messenger/content/AccountManager.js"/>
<script type="application/javascript"
src="chrome://messenger-newsblog/content/am-newsblog.js"/>
<script type="application/javascript"
src="chrome://messenger-newsblog/content/newsblogOverlay.js"/>
<script type="application/javascript"
src="chrome://messenger/content/amUtils.js"/>
<script type="application/javascript"
src="chrome://messenger/content/am-prefs.js"/>
<vbox flex="1" style="overflow: auto;">
<dialogheader id="am-newsblog-title" defaultTitle="&accountTitle.label;"/>
<description class="secDesc">&accountSettingsDesc.label;</description>
<hbox align="center">
<label value="&accountName.label;"
accesskey="&accountName.accesskey;"
control="server.prettyName"/>
<textbox id="server.prettyName"
wsm_persist="true"
size="30"
prefstring="mail.server.%serverkey%.name"/>
</hbox>
<separator class="thin"/>
<groupbox>
<caption label="&serverSettings.label;"/>
<checkbox id="server.loginAtStartUp"
wsm_persist="true"
label="&loginAtStartup.label;"
accesskey="&loginAtStartup.accesskey;"
prefattribute="value"
prefstring="mail.server.%serverkey%.login_at_startup"/>
<hbox align="center">
<checkbox id="server.doBiff"
wsm_persist="true"
label="&biffStart.label;"
accesskey="&biffStart.accesskey;"
oncommand="onCheckItem('server.biffMinutes', [this.id]);"
prefattribute="value"
prefstring="mail.server.%serverkey%.check_new_mail"/>
<textbox id="server.biffMinutes"
wsm_persist="true"
type="number"
size="3"
min="1"
increment="1"
preftype="int"
prefstring="mail.server.%serverkey%.check_time"
aria-labelledby="server.doBiff server.biffMinutes biffEnd"/>
<label id="biffEnd"
value="&biffEnd.label;"
control="server.biffMinutes"/>
</hbox>
<checkbox id="server.quickMode"
wsm_persist="true"
genericattr="true"
label="&useQuickMode.label;"
accesskey="&useQuickMode.accesskey;"
preftype="bool"
prefattribute="value"
prefstring="mail.server.%serverkey%.quickMode"/>
<checkbox id="autotagEnable"
accesskey="&autotagEnable.accesskey;"
label="&autotagEnable.label;"
oncommand="setCategoryPrefs(this)"/>
<hbox>
<checkbox id="autotagUsePrefix"
class="indent"
accesskey="&autotagUsePrefix.accesskey;"
label="&autotagUsePrefix.label;"
oncommand="setCategoryPrefs(this)"/>
<textbox id="autotagPrefix"
placeholder="&autoTagPrefix.placeholder;"
clickSelectsAll="true"
onchange="setCategoryPrefs(this)"/>
</hbox>
</groupbox>
<separator class="thin"/>
<groupbox>
<caption label="&messageStorage.label;"/>
<checkbox id="server.emptyTrashOnExit"
wsm_persist="true"
label="&emptyTrashOnExit.label;"
accesskey="&emptyTrashOnExit.accesskey;"
prefattribute="value"
prefstring="mail.server.%serverkey%.empty_trash_on_exit"/>
<separator class="thin"/>
<vbox>
<label value="&localPath.label;" control="server.localPath"/>
<hbox align="center">
<textbox readonly="true"
wsm_persist="true"
flex="1"
id="server.localPath"
datatype="nsIFile"
prefstring="mail.server.%serverkey%.directory"
class="uri-element"/>
<button id="browseForLocalFolder"
label="&browseFolder.label;"
filepickertitle="&localFolderPicker.label;"
accesskey="&browseFolder.accesskey;"
oncommand="BrowseForLocalFolders();"/>
</hbox>
</vbox>
</groupbox>
<separator class="thin"/>
<hbox align="center">
<spacer flex="1"/>
<button label="&manageSubscriptions.label;"
accesskey="&manageSubscriptions.accesskey;"
oncommand="openSubscriptionsDialog(gServer.rootFolder);"/>
</hbox>
</vbox>
</page>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,235 @@
<?xml version="1.0"?>
<!-- -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://messenger/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://messenger/skin/folderPane.css" type="text/css"?>
<?xml-stylesheet href="chrome://messenger/skin/folderMenus.css" type="text/css"?>
<?xml-stylesheet href="chrome://messenger-newsblog/skin/feed-subscriptions.css" type="text/css"?>
<!DOCTYPE window [
<!ENTITY % feedDTD SYSTEM "chrome://messenger-newsblog/locale/feed-subscriptions.dtd">
%feedDTD;
<!ENTITY % certDTD SYSTEM "chrome://pippki/locale/certManager.dtd">
%certDTD;
]>
<window id="subscriptionsDialog"
flex="1"
title="&feedSubscriptions.label;"
windowtype="Mail:News-BlogSubscriptions"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:nc="http://home.netscape.com/NC-rdf#"
persist="width height screenX screenY sizemode"
onload="FeedSubscriptions.onLoad();"
onclose="return FeedSubscriptions.onClose();"
onkeypress="FeedSubscriptions.onKeyPress(event);"
onmousedown="FeedSubscriptions.onMouseDown(event);">
<script type="application/javascript"
src="chrome://messenger/content/specialTabs.js"/>
<script type="application/javascript"
src="chrome://messenger-newsblog/content/feed-subscriptions.js"/>
<keyset id="extensionsKeys">
<key id="key_close"
key="&cmd.close.commandKey;"
modifiers="accel"
oncommand="window.close();"/>
<key id="key_close2"
keycode="VK_ESCAPE"
oncommand="window.close();"/>
</keyset>
<stringbundle id="bundle_newsblog"
src="chrome://messenger-newsblog/locale/newsblog.properties"/>
<stringbundle id="bundle_brand"
src="chrome://branding/locale/brand.properties"/>
<vbox flex="1" id="contentPane">
<hbox align="right">
<label id="learnMore"
class="text-link"
crop="end"
value="&learnMore.label;"
href="https://support.mozilla.org/kb/how-subscribe-news-feeds-and-blogs"/>
</hbox>
<tree id="rssSubscriptionsList"
treelines="true"
flex="1"
hidecolumnpicker="true"
onselect="FeedSubscriptions.onSelect();"
seltype="single">
<treecols>
<treecol id="folderNameCol"
flex="2"
primary="true"
hideheader="true"/>
</treecols>
<treechildren id="subscriptionChildren"
ondragstart="FeedSubscriptions.onDragStart(event);"
ondragover="FeedSubscriptions.onDragOver(event);"/>
</tree>
<hbox id="rssFeedInfoBox">
<vbox flex="1">
<grid flex="1">
<columns>
<column/>
<column flex="1"/>
</columns>
<rows>
<row>
<hbox align="right" valign="middle">
<label id="nameLabel"
accesskey="&feedTitle.accesskey;"
control="nameValue"
value="&feedTitle.label;"/>
</hbox>
<textbox id="nameValue"
clickSelectsAll="true"/>
</row>
<row>
<hbox align="right" valign="middle">
<label id="locationLabel"
accesskey="&feedLocation.accesskey;"
control="locationValue"
value="&feedLocation.label;"/>
</hbox>
<hbox>
<textbox id="locationValue"
flex="1"
class="uri-element"
placeholder="&feedLocation.placeholder;"
clickSelectsAll="true"
onfocus="FeedSubscriptions.setSummaryFocus();"
onblur="FeedSubscriptions.setSummaryFocus();"/>
<hbox align="center">
<label id="locationValidate"
collapsed="true"
class="text-link"
crop="end"
value="&locationValidate.label;"
onclick="FeedSubscriptions.checkValidation(event);"/>
</hbox>
</hbox>
</row>
<row>
<hbox align="right" valign="middle">
<label id="feedFolderLabel"
value="&feedFolder.label;"
accesskey="&feedFolder.accesskey;"
control="selectFolder"/>
</hbox>
<hbox>
<menulist id="selectFolder"
flex="1"
class="folderMenuItem"
hidden="true">
<menupopup id="selectFolderPopup"
class="menulist-menupopup"
type="folder"
mode="feeds"
showFileHereLabel="true"
showAccountsFileHere="true"
oncommand="FeedSubscriptions.setNewFolder(event)"/>
</menulist>
<textbox id="selectFolderValue"
flex="1"
readonly="true"
onkeypress="FeedSubscriptions.onClickSelectFolderValue(event)"
onclick="FeedSubscriptions.onClickSelectFolderValue(event)"/>
</hbox>
</row>
</rows>
</grid>
<checkbox id="quickMode"
accesskey="&quickMode.accesskey;"
label="&quickMode.label;"
oncommand="FeedSubscriptions.setSummary(this.checked)"/>
<checkbox id="autotagEnable"
accesskey="&autotagEnable.accesskey;"
label="&autotagEnable.label;"
oncommand="FeedSubscriptions.setCategoryPrefs(this)"/>
<hbox>
<checkbox id="autotagUsePrefix"
class="indent"
accesskey="&autotagUsePrefix.accesskey;"
label="&autotagUsePrefix.label;"
oncommand="FeedSubscriptions.setCategoryPrefs(this)"/>
<textbox id="autotagPrefix"
placeholder="&autoTagPrefix.placeholder;"
clickSelectsAll="true"/>
</hbox>
<separator class="thin"/>
</vbox>
</hbox>
<hbox id="statusContainerBox"
align="center"
valign="middle">
<vbox flex="1">
<description id="statusText"/>
</vbox>
<spacer flex="1"/>
<label id="validationText"
collapsed="true"
class="text-link"
crop="end"
value="&validateText.label;"
onclick="FeedSubscriptions.checkValidation(event);"/>
<button id="addCertException"
collapsed="true"
label="&certmgr.addException.label;"
accesskey="&certmgr.addException.accesskey;"
oncommand="FeedSubscriptions.addCertExceptionDialog();"/>
<progressmeter id="progressMeter"
collapsed="true"
mode="determined"
value="0"/>
</hbox>
<hbox align="end">
<hbox class="actionButtons" flex="1">
<button id="addFeed"
label="&button.addFeed.label;"
accesskey="&button.addFeed.accesskey;"
oncommand="FeedSubscriptions.addFeed();"/>
<button id="editFeed"
disabled="true"
label="&button.updateFeed.label;"
accesskey="&button.updateFeed.accesskey;"
oncommand="FeedSubscriptions.editFeed();"/>
<button id="removeFeed"
disabled="true"
label="&button.removeFeed.label;"
accesskey="&button.removeFeed.accesskey;"
oncommand="FeedSubscriptions.removeFeed(true);"/>
<button id="importOPML"
label="&button.importOPML.label;"
accesskey="&button.importOPML.accesskey;"
oncommand="FeedSubscriptions.importOPML();"/>
<button id="exportOPML"
label="&button.exportOPML.label;"
accesskey="&button.exportOPML.accesskey;"
tooltiptext="&button.exportOPML.tooltip;"
oncommand="FeedSubscriptions.exportOPML(event);"/>
<spacer flex="1"/>
<button id="close"
label="&button.close.label;"
icon="close"
oncommand="if (FeedSubscriptions.onClose()) window.close();"/>
</hbox>
</hbox>
</vbox>
</window>

View file

@ -0,0 +1,45 @@
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Components.utils.import("resource:///modules/FeedUtils.jsm");
/* Feed account standalone wizard functions */
var FeedAccountWizard = {
accountName: "",
accountSetupPageInit: function() {
this.accountSetupPageValidate();
},
accountSetupPageValidate: function() {
this.accountName = document.getElementById("prettyName").value.trim();
document.documentElement.canAdvance = this.accountName;
},
accountSetupPageUnload: function() {
return;
},
donePageInit: function() {
document.getElementById("account.name.text").value = this.accountName;
},
onCancel: function() {
return true;
},
onFinish: function() {
let account = FeedUtils.createRssAccount(this.accountName);
if ("gFolderTreeView" in window.opener.top)
// Opened from 3pane File->New or Appmenu New Message, or
// Account Central link.
window.opener.top.gFolderTreeView.selectFolder(account.incomingServer.rootMsgFolder);
else if ("selectServer" in window.opener)
// Opened from Account Settings.
window.opener.selectServer(account.incomingServer);
window.close();
}
}

View file

@ -0,0 +1,79 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://messenger/skin/accountWizard.css" type="text/css"?>
<!DOCTYPE wizard [
<!ENTITY % accountDTD SYSTEM "chrome://messenger/locale/AccountWizard.dtd">
%accountDTD;
<!ENTITY % newsblogDTD SYSTEM "chrome://messenger-newsblog/locale/am-newsblog.dtd" >
%newsblogDTD;
<!ENTITY % imDTD SYSTEM "chrome://messenger/locale/imAccountWizard.dtd" >
%imDTD;
]>
<wizard id="FeedAccountWizard"
title="&feedWindowTitle.label;"
onwizardcancel="return FeedAccountWizard.onCancel();"
onwizardfinish="return FeedAccountWizard.onFinish();"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript"
src="chrome://messenger-newsblog/content/feedAccountWizard.js"/>
<!-- Account setup page : User gets a choice to enter a name for the account -->
<!-- Defaults : Feed account name -> default string -->
<wizardpage id="accountsetuppage"
pageid="accountsetuppage"
label="&accnameTitle.label;"
onpageshow="return FeedAccountWizard.accountSetupPageInit();"
onpageadvanced="return FeedAccountWizard.accountSetupPageUnload();">
<vbox flex="1">
<description>&accnameDesc.label;</description>
<separator class="thin"/>
<hbox align="center">
<label class="label"
value="&accnameLabel.label;"
accesskey="&accnameLabel.accesskey;"
control="prettyName"/>
<textbox id="prettyName"
flex="1"
value="&feeds.accountName;"
oninput="FeedAccountWizard.accountSetupPageValidate();"/>
</hbox>
</vbox>
</wizardpage>
<!-- Done page : Summarizes information collected to create a feed account -->
<wizardpage id="done"
pageid="done"
label="&accountSummaryTitle.label;"
onpageshow="return FeedAccountWizard.donePageInit();">
<vbox flex="1">
<description>&accountSummaryInfo.label;</description>
<separator class="thin"/>
<grid>
<columns>
<column/>
<column flex="1"/>
</columns>
<rows>
<row id="account.name"
align="center">
<label id="account.name.label"
class="label"
flex="1"
value="&accnameLabel.label;"/>
<label id="account.name.text"
class="label"/>
</row>
</rows>
</grid>
<separator/>
<spacer flex="1"/>
</vbox>
</wizardpage>
</wizard>

View file

@ -0,0 +1,363 @@
/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Components.utils.import("resource:///modules/gloda/mimemsg.js");
Components.utils.import("resource:///modules/mailServices.js");
Components.utils.import("resource://gre/modules/Services.jsm");
// This global is for SeaMonkey compatibility.
var gShowFeedSummary;
var FeedMessageHandler = {
gShowSummary: true,
gToggle: false,
kSelectOverrideWebPage: 0,
kSelectOverrideSummary: 1,
kSelectFeedDefault: 2,
kOpenWebPage: 0,
kOpenSummary: 1,
kOpenToggleInMessagePane: 2,
kOpenLoadInBrowser: 3,
/**
* How to load message on threadpane select.
*/
get onSelectPref() {
return Services.prefs.getIntPref("rss.show.summary");
},
set onSelectPref(val) {
Services.prefs.setIntPref("rss.show.summary", val);
ReloadMessage();
},
/**
* Load web page on threadpane select.
*/
get loadWebPageOnSelectPref() {
return Services.prefs.getIntPref("rss.message.loadWebPageOnSelect") ? true : false;
},
/**
* How to load message on open (enter/dbl click in threadpane, contextmenu).
*/
get onOpenPref() {
return Services.prefs.getIntPref("rss.show.content-base");
},
set onOpenPref(val) {
Services.prefs.setIntPref("rss.show.content-base", val);
},
/**
* Determine if a message is a feed message. Prior to Tb15, a message had to
* be in an rss acount type folder. In Tb15 and later, a flag is set on the
* message itself upon initial store; the message can be moved to any folder.
*
* @param nsIMsgDBHdr aMsgHdr - the message.
*
* @return true if message is a feed, false if not.
*/
isFeedMessage: function(aMsgHdr) {
return (aMsgHdr instanceof Components.interfaces.nsIMsgDBHdr) &&
((aMsgHdr.flags & Components.interfaces.nsMsgMessageFlags.FeedMsg) ||
(aMsgHdr.folder && aMsgHdr.folder.server.type == "rss"));
},
/**
* Determine whether to show a feed message summary or load a web page in the
* message pane.
*
* @param nsIMsgDBHdr aMsgHdr - the message.
* @param bool aToggle - true if in toggle mode, false otherwise.
*
* @return true if summary is to be displayed, false if web page.
*/
shouldShowSummary: function(aMsgHdr, aToggle) {
// Not a feed message, always show summary (the message).
if (!this.isFeedMessage(aMsgHdr))
return true;
// Notified of a summary reload when toggling, reset toggle and return.
if (!aToggle && this.gToggle)
return !(this.gToggle = false);
let showSummary = true;
this.gToggle = aToggle;
// Thunderbird 2 rss messages with 'Show article summary' not selected,
// ie message body constructed to show web page in an iframe, can't show
// a summary - notify user.
let browser = getBrowser();
let contentDoc = browser ? browser.contentDocument : null;
let rssIframe = contentDoc ? contentDoc.getElementById("_mailrssiframe") : null;
if (rssIframe) {
if (this.gToggle || this.onSelectPref == this.kSelectOverrideSummary)
this.gToggle = false;
return false;
}
if (aToggle)
// Toggle mode, flip value.
return gShowFeedSummary = this.gShowSummary = !this.gShowSummary;
let wintype = document.documentElement.getAttribute("windowtype");
let tabMail = document.getElementById("tabmail");
let messageTab = tabMail && tabMail.currentTabInfo.mode.type == "message";
let messageWindow = wintype == "mail:messageWindow";
switch (this.onSelectPref) {
case this.kSelectOverrideWebPage:
showSummary = false;
break;
case this.kSelectOverrideSummary:
showSummary = true
break;
case this.kSelectFeedDefault:
// Get quickmode per feed folder pref from feeds.rdf. If the feed
// message is not in a feed account folder (hence the folder is not in
// the feeds database), or FZ_QUICKMODE property is not found (possible
// in pre renovation urls), err on the side of showing the summary.
// For the former, toggle or global override is necessary; for the
// latter, a show summary checkbox toggle in Subscribe dialog will set
// one on the path to bliss.
let folder = aMsgHdr.folder, targetRes;
try {
targetRes = FeedUtils.getParentTargetForChildResource(
folder.URI, FeedUtils.FZ_QUICKMODE, folder.server);
}
catch (ex) {
// Not in a feed account folder or other error.
FeedUtils.log.info("FeedMessageHandler.shouldShowSummary: could not " +
"get summary pref for this folder");
}
showSummary = targetRes && targetRes.QueryInterface(Ci.nsIRDFLiteral).
Value == "false" ? false : true;
break;
}
gShowFeedSummary = this.gShowSummary = showSummary;
if (messageWindow || messageTab) {
// Message opened in either standalone window or tab, due to either
// message open pref (we are here only if the pref is 0 or 1) or
// contextmenu open.
switch (this.onOpenPref) {
case this.kOpenToggleInMessagePane:
// Opened by contextmenu, use the value derived above.
// XXX: allow a toggle via crtl?
break;
case this.kOpenWebPage:
showSummary = false;
break;
case this.kOpenSummary:
showSummary = true;
break;
}
}
// Auto load web page in browser on select, per pref; shouldShowSummary() is
// always called first to 1)test if feed, 2)get summary pref, so do it here.
if (this.loadWebPageOnSelectPref)
setTimeout(FeedMessageHandler.loadWebPage, 20, aMsgHdr, {browser:true});
return showSummary;
},
/**
* Load a web page for feed messages. Use MsgHdrToMimeMessage() to get
* the content-base url from the message headers. We cannot rely on
* currentHeaderData; it has not yet been streamed at our entry point in
* displayMessageChanged(), and in the case of a collapsed message pane it
* is not streamed.
*
* @param nsIMsgDBHdr aMessageHdr - the message.
* @param {obj} aWhere - name value=true pair, where name is in:
* 'messagepane', 'browser', 'tab', 'window'.
*/
loadWebPage: function(aMessageHdr, aWhere) {
MsgHdrToMimeMessage(aMessageHdr, null, function(aMsgHdr, aMimeMsg) {
if (aMimeMsg && aMimeMsg.headers["content-base"] &&
aMimeMsg.headers["content-base"][0]) {
let url = aMimeMsg.headers["content-base"], uri;
try {
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
url = converter.ConvertToUnicode(url);
uri = Services.io.newURI(url, null, null);
url = uri.spec;
}
catch (ex) {
FeedUtils.log.info("FeedMessageHandler.loadWebPage: " +
"invalid Content-Base header url - " + url);
return;
}
if (aWhere.browser)
Components.classes["@mozilla.org/uriloader/external-protocol-service;1"]
.getService(Components.interfaces.nsIExternalProtocolService)
.loadURI(uri);
else if (aWhere.messagepane) {
let loadFlag = getBrowser().webNavigation.LOAD_FLAGS_NONE;
getBrowser().webNavigation.loadURI(url, loadFlag, null, null, null);
}
else if (aWhere.tab)
openContentTab(url, "tab", "^");
else if (aWhere.window)
openContentTab(url, "window", "^");
}
else
FeedUtils.log.info("FeedMessageHandler.loadWebPage: could not get " +
"Content-Base header url for this message");
});
},
/**
* Display summary or load web page for feed messages. Caller should already
* know if the message is a feed message.
*
* @param nsIMsgDBHdr aMsgHdr - the message.
* @param bool aShowSummary - true if summary is to be displayed, false if
* web page.
*/
setContent: function(aMsgHdr, aShowSummary) {
if (aShowSummary) {
// Only here if toggling to summary in 3pane.
if (this.gToggle && gDBView && GetNumSelectedMessages() == 1)
ReloadMessage();
}
else {
let browser = getBrowser();
if (browser && browser.contentDocument && browser.contentDocument.body)
browser.contentDocument.body.hidden = true;
// If in a non rss folder, hide possible remote content bar on a web
// page load, as it doesn't apply.
if ("msgNotificationBar" in window)
gMessageNotificationBar.clearMsgNotifications();
this.loadWebPage(aMsgHdr, {messagepane:true});
this.gToggle = false;
}
}
}
function openSubscriptionsDialog(aFolder)
{
// Check for an existing feed subscriptions window and focus it.
let subscriptionsWindow =
Services.wm.getMostRecentWindow("Mail:News-BlogSubscriptions");
if (subscriptionsWindow)
{
if (aFolder)
{
subscriptionsWindow.FeedSubscriptions.selectFolder(aFolder);
subscriptionsWindow.FeedSubscriptions.mView.treeBox.ensureRowIsVisible(
subscriptionsWindow.FeedSubscriptions.mView.selection.currentIndex);
}
subscriptionsWindow.focus();
}
else
{
window.openDialog("chrome://messenger-newsblog/content/feed-subscriptions.xul",
"", "centerscreen,chrome,dialog=no,resizable",
{ folder: aFolder});
}
}
// Special case attempts to reply/forward/edit as new RSS articles. For
// messages stored prior to Tb15, we are here only if the message's folder's
// account server is rss and feed messages moved to other types will have their
// summaries loaded, as viewing web pages only happened in an rss account.
// The user may choose whether to load a summary or web page link by ensuring
// the current feed message is being viewed as either a summary or web page.
function openComposeWindowForRSSArticle(aMsgComposeWindow, aMsgHdr, aMessageUri,
aType, aFormat, aIdentity, aMsgWindow)
{
// Ensure right content is handled for web pages in window/tab.
let tabmail = document.getElementById("tabmail");
let is3pane = tabmail && tabmail.selectedTab && tabmail.selectedTab.mode ?
tabmail.selectedTab.mode.type == "folder" : false;
let showingwebpage = ("FeedMessageHandler" in window) && !is3pane &&
FeedMessageHandler.onOpenPref == FeedMessageHandler.kOpenWebPage;
if (gShowFeedSummary && !showingwebpage)
{
// The user is viewing the summary.
MailServices.compose.OpenComposeWindow(aMsgComposeWindow, aMsgHdr, aMessageUri,
aType, aFormat, aIdentity, aMsgWindow);
}
else
{
// Set up the compose message and get the feed message's web page link.
let Cc = Components.classes;
let Ci = Components.interfaces;
let msgHdr = aMsgHdr;
let type = aType;
let msgComposeType = Ci.nsIMsgCompType;
let subject = msgHdr.mime2DecodedSubject;
let fwdPrefix = Services.prefs.getCharPref("mail.forward_subject_prefix");
fwdPrefix = fwdPrefix ? fwdPrefix + ": " : "";
let params = Cc["@mozilla.org/messengercompose/composeparams;1"]
.createInstance(Ci.nsIMsgComposeParams);
let composeFields = Cc["@mozilla.org/messengercompose/composefields;1"]
.createInstance(Ci.nsIMsgCompFields);
if (type == msgComposeType.Reply ||
type == msgComposeType.ReplyAll ||
type == msgComposeType.ReplyToSender ||
type == msgComposeType.ReplyToGroup ||
type == msgComposeType.ReplyToSenderAndGroup)
{
subject = "Re: " + subject;
}
else if (type == msgComposeType.ForwardInline ||
type == msgComposeType.ForwardAsAttachment)
{
subject = fwdPrefix + subject;
}
params.composeFields = composeFields;
params.composeFields.subject = subject;
params.composeFields.characterSet = msgHdr.Charset;
params.composeFields.body = "";
params.bodyIsLink = false;
params.identity = aIdentity;
try
{
// The feed's web page url is stored in the Content-Base header.
MsgHdrToMimeMessage(msgHdr, null, function(aMsgHdr, aMimeMsg) {
if (aMimeMsg && aMimeMsg.headers["content-base"] &&
aMimeMsg.headers["content-base"][0])
{
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
let url = converter.ConvertToUnicode(aMimeMsg.headers["content-base"]);
params.composeFields.body = url;
params.bodyIsLink = true;
MailServices.compose.OpenComposeWindowWithParams(null, params);
}
else
// No content-base url, use the summary.
MailServices.compose.OpenComposeWindow(aMsgComposeWindow, aMsgHdr, aMessageUri,
aType, aFormat, aIdentity, aMsgWindow);
}, false, {saneBodySize: true});
}
catch (ex)
{
// Error getting header, use the summary.
MailServices.compose.OpenComposeWindow(aMsgComposeWindow, aMsgHdr, aMessageUri,
aType, aFormat, aIdentity, aMsgWindow);
}
}
}

View file

@ -0,0 +1,16 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
newsblog.jar:
% content messenger-newsblog %content/messenger-newsblog/
content/messenger-newsblog/newsblogOverlay.js (content/newsblogOverlay.js)
content/messenger-newsblog/Feed.js (content/Feed.js)
content/messenger-newsblog/FeedItem.js (content/FeedItem.js)
content/messenger-newsblog/feed-parser.js (content/feed-parser.js)
* content/messenger-newsblog/feed-subscriptions.js (content/feed-subscriptions.js)
content/messenger-newsblog/feed-subscriptions.xul (content/feed-subscriptions.xul)
content/messenger-newsblog/am-newsblog.js (content/am-newsblog.js)
content/messenger-newsblog/am-newsblog.xul (content/am-newsblog.xul)
content/messenger-newsblog/feedAccountWizard.js (content/feedAccountWizard.js)
content/messenger-newsblog/feedAccountWizard.xul (content/feedAccountWizard.xul)

View file

@ -0,0 +1,99 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource:///modules/FeedUtils.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
var nsNewsBlogFeedDownloader =
{
downloadFeed: function(aFolder, aUrlListener, aIsBiff, aMsgWindow)
{
FeedUtils.downloadFeed(aFolder, aUrlListener, aIsBiff, aMsgWindow);
},
subscribeToFeed: function(aUrl, aFolder, aMsgWindow)
{
FeedUtils.subscribeToFeed(aUrl, aFolder, aMsgWindow);
},
updateSubscriptionsDS: function(aFolder, aOrigFolder, aAction)
{
FeedUtils.updateSubscriptionsDS(aFolder, aOrigFolder, aAction);
},
QueryInterface: function(aIID)
{
if (aIID.equals(Ci.nsINewsBlogFeedDownloader) ||
aIID.equals(Ci.nsISupports))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
}
}
var nsNewsBlogAcctMgrExtension =
{
name: "newsblog",
chromePackageName: "messenger-newsblog",
showPanel: function (server)
{
return false;
},
QueryInterface: function(aIID)
{
if (aIID.equals(Ci.nsIMsgAccountManagerExtension) ||
aIID.equals(Ci.nsISupports))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
}
}
function FeedDownloader() {}
FeedDownloader.prototype =
{
classID: Components.ID("{5c124537-adca-4456-b2b5-641ab687d1f6}"),
_xpcom_factory:
{
createInstance: function (aOuter, aIID)
{
if (aOuter != null)
throw Cr.NS_ERROR_NO_AGGREGATION;
if (!aIID.equals(Ci.nsINewsBlogFeedDownloader) &&
!aIID.equals(Ci.nsISupports))
throw Cr.NS_ERROR_INVALID_ARG;
// return the singleton
return nsNewsBlogFeedDownloader.QueryInterface(aIID);
}
} // factory
}; // feed downloader
function AcctMgrExtension() {}
AcctMgrExtension.prototype =
{
classID: Components.ID("{E109C05F-D304-4ca5-8C44-6DE1BFAF1F74}"),
_xpcom_factory:
{
createInstance: function (aOuter, aIID)
{
if (aOuter != null)
throw Cr.NS_ERROR_NO_AGGREGATION;
if (!aIID.equals(Ci.nsIMsgAccountManagerExtension) &&
!aIID.equals(Ci.nsISupports))
throw Cr.NS_ERROR_INVALID_ARG;
// return the singleton
return nsNewsBlogAcctMgrExtension.QueryInterface(aIID);
}
} // factory
}; // account manager extension
var components = [FeedDownloader, AcctMgrExtension];
var NSGetFactory = XPCOMUtils.generateNSGetFactory(components);

View file

@ -0,0 +1,5 @@
component {5c124537-adca-4456-b2b5-641ab687d1f6} newsblog.js
contract @mozilla.org/newsblog-feed-downloader;1 {5c124537-adca-4456-b2b5-641ab687d1f6}
component {E109C05F-D304-4ca5-8C44-6DE1BFAF1F74} newsblog.js
contract @mozilla.org/accountmanager/extension;1?name=newsblog {E109C05F-D304-4ca5-8C44-6DE1BFAF1F74}
category mailnews-accountmanager-extensions newsblog @mozilla.org/accountmanager/extension;1?name=newsblog

View file

@ -0,0 +1,18 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXTRA_COMPONENTS += [
'js/newsblog.js',
'js/newsblog.manifest',
]
EXTRA_JS_MODULES += [
'content/FeedUtils.jsm',
]
JAR_MANIFESTS += ['jar.mn']
FINAL_TARGET_FILES.isp += [
'rss.rdf',
]

View file

@ -0,0 +1,43 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE RDF SYSTEM "chrome://messenger-newsblog/locale/am-newsblog.dtd">
<RDF:RDF
xmlns:NC="http://home.netscape.com/NC-rdf#"
xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<RDF:Description about="NC:ispinfo">
<NC:providers>
<NC:nsIMsgAccount about="newsblog">
<!-- server info -->
<NC:incomingServer>
<NC:nsIMsgIncomingServer>
<NC:hostName>Feeds</NC:hostName>
<NC:type>rss</NC:type>
<NC:biffMinutes>100</NC:biffMinutes>
<NC:username>nobody</NC:username>
</NC:nsIMsgIncomingServer>
</NC:incomingServer>
<!-- identity defaults -->
<NC:identity>
<NC:nsIMsgIdentity>
</NC:nsIMsgIdentity>
</NC:identity>
<NC:wizardAutoGenerateUniqueHostname>true</NC:wizardAutoGenerateUniqueHostname>
<NC:wizardHideIncoming>true</NC:wizardHideIncoming>
<NC:wizardAccountName>&feeds.accountName;</NC:wizardAccountName>
<NC:wizardSkipPanels>identitypage,incomingpage,outgoingpage</NC:wizardSkipPanels>
<NC:wizardShortName>&feeds.wizardShortName;</NC:wizardShortName>
<NC:wizardLongName>&feeds.wizardLongName;</NC:wizardLongName>
<NC:wizardLongNameAccesskey>&feeds.wizardLongName.accesskey;</NC:wizardLongNameAccesskey>
<NC:wizardShow>true</NC:wizardShow>
<NC:emailProviderName>RSS</NC:emailProviderName>
<NC:showServerDetailsOnWizardSummary>false</NC:showServerDetailsOnWizardSummary>
</NC:nsIMsgAccount>
</NC:providers>
</RDF:Description>
</RDF:RDF>

View file

@ -0,0 +1,170 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
var kDebug = false;
var kOfflineStartupPref = "offline.startup_state";
var kRememberLastState = 0;
var kAskForOnlineState = 1;
var kAlwaysOnline = 2;
var kAlwaysOffline = 3;
var kAutomatic = 4;
var gStartingUp = true;
var gOfflineStartupMode; //0 = remember last state, 1 = ask me, 2 == online, 3 == offline, 4 = automatic
////////////////////////////////////////////////////////////////////////
//
// nsOfflineStartup : nsIObserver
//
// Check if the user has set the pref to be prompted for
// online/offline startup mode. If so, prompt the user. Also,
// check if the user wants to remember their offline state
// the next time they start up.
// If the user shutdown offline, and is now starting up in online
// mode, we will set the boolean pref "mailnews.playback_offline" to true.
//
////////////////////////////////////////////////////////////////////////
var nsOfflineStartup =
{
onProfileStartup: function()
{
debug("onProfileStartup");
if (gStartingUp)
{
gStartingUp = false;
// if checked, the "work offline" checkbox overrides
if (Services.io.offline && !Services.io.manageOfflineStatus)
{
debug("already offline!");
return;
}
}
var manageOfflineStatus = Services.prefs.getBoolPref("offline.autoDetect");
gOfflineStartupMode = Services.prefs.getIntPref(kOfflineStartupPref);
let wasOffline = !Services.prefs.getBoolPref("network.online");
if (gOfflineStartupMode == kAutomatic)
{
// Offline state should be managed automatically
// so do nothing specific at startup.
}
else if (gOfflineStartupMode == kAlwaysOffline)
{
Services.io.manageOfflineStatus = false;
Services.io.offline = true;
}
else if (gOfflineStartupMode == kAlwaysOnline)
{
Services.io.manageOfflineStatus = manageOfflineStatus;
if (wasOffline)
Services.prefs.setBoolPref("mailnews.playback_offline", true);
// If we're managing the offline status, don't force online here... it may
// be the network really is offline.
if (!manageOfflineStatus)
Services.io.offline = false;
}
else if (gOfflineStartupMode == kRememberLastState)
{
Services.io.manageOfflineStatus = manageOfflineStatus && !wasOffline;
// If we are meant to be online, and managing the offline status
// then don't force it - it may be the network really is offline.
if (!manageOfflineStatus || wasOffline)
Services.io.offline = wasOffline;
}
else if (gOfflineStartupMode == kAskForOnlineState)
{
var bundle = Services.strings.createBundle("chrome://messenger/locale/offlineStartup.properties");
var title = bundle.GetStringFromName("title");
var desc = bundle.GetStringFromName("desc");
var button0Text = bundle.GetStringFromName("workOnline");
var button1Text = bundle.GetStringFromName("workOffline");
var checkVal = {value:0};
var result = Services.prompt.confirmEx(null, title, desc,
(Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_IS_STRING) +
(Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_IS_STRING),
button0Text, button1Text, null, null, checkVal);
debug ("result = " + result + "\n");
Services.io.manageOfflineStatus = manageOfflineStatus && result != 1;
Services.io.offline = result == 1;
if (result != 1 && wasOffline)
Services.prefs.setBoolPref("mailnews.playback_offline", true);
}
},
observe: function(aSubject, aTopic, aData)
{
debug("observe: " + aTopic);
if (aTopic == "profile-change-net-teardown")
{
debug("remembering offline state");
Services.prefs.setBoolPref("network.online", !Services.io.offline);
}
else if (aTopic == "app-startup")
{
Services.obs.addObserver(this, "profile-after-change", false);
Services.obs.addObserver(this, "profile-change-net-teardown", false);
}
else if (aTopic == "profile-after-change")
{
this.onProfileStartup();
}
},
QueryInterface: function(aIID)
{
if (aIID.equals(Components.interfaces.nsIObserver) ||
aIID.equals(Components.interfaces.nsISupports))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
}
}
function nsOfflineStartupModule()
{
}
nsOfflineStartupModule.prototype =
{
classID: Components.ID("3028a3c8-2165-42a4-b878-398da5d32736"),
_xpcom_factory:
{
createInstance: function(aOuter, aIID)
{
if (aOuter != null)
throw Components.results.NS_ERROR_NO_AGGREGATION;
// return the singleton
return nsOfflineStartup.QueryInterface(aIID);
},
lockFactory: function(aLock)
{
// quieten warnings
}
}
};
////////////////////////////////////////////////////////////////////////
//
// Debug helper
//
////////////////////////////////////////////////////////////////////////
if (!kDebug)
debug = function(m) {};
else
debug = function(m) {dump("\t *** nsOfflineStartup: " + m + "\n");};
var components = [nsOfflineStartupModule];
var NSGetFactory = XPCOMUtils.generateNSGetFactory(components);

View file

@ -0,0 +1,3 @@
component {3028a3c8-2165-42a4-b878-398da5d32736} offlineStartup.js
contract @mozilla.org/offline-startup;1 {3028a3c8-2165-42a4-b878-398da5d32736}
category app-startup Offline-startup @mozilla.org/offline-startup;1

View file

@ -0,0 +1,10 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXTRA_COMPONENTS += [
'js/offlineStartup.js',
'js/offlineStartup.manifest',
]

View file

@ -0,0 +1,478 @@
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Components.utils.import("resource://gre/modules/Services.jsm");
var nsIX509CertDB = Components.interfaces.nsIX509CertDB;
var nsX509CertDBContractID = "@mozilla.org/security/x509certdb;1";
var nsIX509Cert = Components.interfaces.nsIX509Cert;
var email_recipient_cert_usage = 5;
var email_signing_cert_usage = 4;
var gIdentity;
var gPref = null;
var gEncryptionCertName = null;
var gHiddenEncryptionPolicy = null;
var gEncryptionChoices = null;
var gSignCertName = null;
var gSignMessages = null;
var gEncryptAlways = null;
var gNeverEncrypt = null;
var gBundle = null;
var gBrandBundle;
var gSmimePrefbranch;
var gEncryptionChoicesLocked;
var gSigningChoicesLocked;
var kEncryptionCertPref = "identity.encryption_cert_name";
var kSigningCertPref = "identity.signing_cert_name";
function onInit()
{
smimeInitializeFields();
}
function smimeInitializeFields()
{
// initialize all of our elements based on the current identity values....
gEncryptionCertName = document.getElementById(kEncryptionCertPref);
gHiddenEncryptionPolicy = document.getElementById("identity.encryptionpolicy");
gEncryptionChoices = document.getElementById("encryptionChoices");
gSignCertName = document.getElementById(kSigningCertPref);
gSignMessages = document.getElementById("identity.sign_mail");
gEncryptAlways = document.getElementById("encrypt_mail_always");
gNeverEncrypt = document.getElementById("encrypt_mail_never");
gBundle = document.getElementById("bundle_smime");
gBrandBundle = document.getElementById("bundle_brand");
gEncryptionChoicesLocked = false;
gSigningChoicesLocked = false;
if (!gIdentity) {
// The user is going to create a new identity.
// Set everything to default values.
// Do not take over the values from gAccount.defaultIdentity
// as the new identity is going to have a different mail address.
gEncryptionCertName.value = "";
gEncryptionCertName.nickname = "";
gEncryptionCertName.dbKey = "";
gSignCertName.value = "";
gSignCertName.nickname = "";
gSignCertName.dbKey = "";
gEncryptAlways.setAttribute("disabled", true);
gNeverEncrypt.setAttribute("disabled", true);
gSignMessages.setAttribute("disabled", true);
gSignMessages.checked = false;
gEncryptionChoices.value = 0;
}
else {
var certdb = Components.classes[nsX509CertDBContractID].getService(nsIX509CertDB);
var x509cert = null;
gEncryptionCertName.value = gIdentity.getUnicharAttribute("encryption_cert_name");
gEncryptionCertName.dbKey = gIdentity.getCharAttribute("encryption_cert_dbkey");
// If we succeed in looking up the certificate by the dbkey pref, then
// append the serial number " [...]" to the display value, and remember the
// nickname in a separate property.
try {
if (certdb && gEncryptionCertName.dbKey &&
(x509cert = certdb.findCertByDBKey(gEncryptionCertName.dbKey))) {
gEncryptionCertName.value = x509cert.nickname + " [" + x509cert.serialNumber + "]";
gEncryptionCertName.nickname = x509cert.nickname;
}
} catch(e) {}
gEncryptionChoices.value = gIdentity.getIntAttribute("encryptionpolicy");
if (!gEncryptionCertName.value) {
gEncryptAlways.setAttribute("disabled", true);
gNeverEncrypt.setAttribute("disabled", true);
}
else {
enableEncryptionControls(true);
}
gSignCertName.value = gIdentity.getUnicharAttribute("signing_cert_name");
gSignCertName.dbKey = gIdentity.getCharAttribute("signing_cert_dbkey");
x509cert = null;
// same procedure as with gEncryptionCertName (see above)
try {
if (certdb && gSignCertName.dbKey &&
(x509cert = certdb.findCertByDBKey(gSignCertName.dbKey))) {
gSignCertName.value = x509cert.nickname + " [" + x509cert.serialNumber + "]";
gSignCertName.nickname = x509cert.nickname;
}
} catch(e) {}
gSignMessages.checked = gIdentity.getBoolAttribute("sign_mail");
if (!gSignCertName.value)
{
gSignMessages.setAttribute("disabled", true);
}
else {
enableSigningControls(true);
}
}
// Always start with enabling signing and encryption cert select buttons.
// This will keep the visibility of buttons in a sane state as user
// jumps from security panel of one account to another.
enableCertSelectButtons();
// Disable all locked elements on the panel
if (gIdentity)
onLockPreference();
}
function onPreInit(account, accountValues)
{
gIdentity = account.defaultIdentity;
}
function onSave()
{
smimeSave();
}
function smimeSave()
{
// find out which radio for the encryption radio group is selected and set that on our hidden encryptionChoice pref....
var newValue = gEncryptionChoices.value;
gHiddenEncryptionPolicy.setAttribute('value', newValue);
gIdentity.setIntAttribute("encryptionpolicy", newValue);
gIdentity.setUnicharAttribute("encryption_cert_name",
gEncryptionCertName.nickname || gEncryptionCertName.value);
gIdentity.setCharAttribute("encryption_cert_dbkey", gEncryptionCertName.dbKey);
gIdentity.setBoolAttribute("sign_mail", gSignMessages.checked);
gIdentity.setUnicharAttribute("signing_cert_name",
gSignCertName.nickname || gSignCertName.value);
gIdentity.setCharAttribute("signing_cert_dbkey", gSignCertName.dbKey);
}
function smimeOnAcceptEditor()
{
try {
if (!onOk())
return false;
}
catch (ex) {}
smimeSave();
return true;
}
function onLockPreference()
{
var initPrefString = "mail.identity";
var finalPrefString;
var allPrefElements = [
{ prefstring:"signingCertSelectButton", id:"signingCertSelectButton"},
{ prefstring:"encryptionCertSelectButton", id:"encryptionCertSelectButton"},
{ prefstring:"sign_mail", id:"identity.sign_mail"},
{ prefstring:"encryptionpolicy", id:"encryptionChoices"}
];
finalPrefString = initPrefString + "." + gIdentity.key + ".";
gSmimePrefbranch = Services.prefs.getBranch(finalPrefString);
disableIfLocked( allPrefElements );
}
// Does the work of disabling an element given the array which contains xul id/prefstring pairs.
// Also saves the id/locked state in an array so that other areas of the code can avoid
// stomping on the disabled state indiscriminately.
function disableIfLocked( prefstrArray )
{
var i;
for (i=0; i<prefstrArray.length; i++) {
var id = prefstrArray[i].id;
var element = document.getElementById(id);
if (gSmimePrefbranch.prefIsLocked(prefstrArray[i].prefstring)) {
// If encryption choices radio group is locked, make sure the individual
// choices in the group are locked. Set a global (gEncryptionChoicesLocked)
// indicating the status so that locking can be maintained further.
if (id == "encryptionChoices") {
document.getElementById("encrypt_mail_never").setAttribute("disabled", "true");
document.getElementById("encrypt_mail_always").setAttribute("disabled", "true");
gEncryptionChoicesLocked = true;
}
// If option to sign mail is locked (with true/false set in config file), disable
// the corresponding checkbox and set a global (gSigningChoicesLocked) in order to
// honor the locking as user changes other elements on the panel.
if (id == "identity.sign_mail") {
document.getElementById("identity.sign_mail").setAttribute("disabled", "true");
gSigningChoicesLocked = true;
}
else {
element.setAttribute("disabled", "true");
if (id == "signingCertSelectButton") {
document.getElementById("signingCertClearButton").setAttribute("disabled", "true");
}
else if (id == "encryptionCertSelectButton") {
document.getElementById("encryptionCertClearButton").setAttribute("disabled", "true");
}
}
}
}
}
function alertUser(message)
{
Services.prompt.alert(window,
gBrandBundle.getString("brandShortName"),
message);
}
function askUser(message)
{
let button = Services.prompt.confirmEx(
window,
gBrandBundle.getString("brandShortName"),
message,
Services.prompt.STD_YES_NO_BUTTONS,
null,
null,
null,
null,
{});
// confirmEx returns button index:
return (button == 0);
}
function checkOtherCert(cert, pref, usage, msgNeedCertWantSame, msgWantSame, msgNeedCertWantToSelect, enabler)
{
var otherCertInfo = document.getElementById(pref);
if (!otherCertInfo)
return;
if (otherCertInfo.dbKey == cert.dbKey)
// all is fine, same cert is now selected for both purposes
return;
var certdb = Components.classes[nsX509CertDBContractID].getService(nsIX509CertDB);
if (!certdb)
return;
if (email_recipient_cert_usage == usage) {
matchingOtherCert = certdb.findEmailEncryptionCert(cert.nickname);
}
else if (email_signing_cert_usage == usage) {
matchingOtherCert = certdb.findEmailSigningCert(cert.nickname);
}
else
return;
var userWantsSameCert = false;
if (!otherCertInfo.value.length) {
if (matchingOtherCert && (matchingOtherCert.dbKey == cert.dbKey)) {
userWantsSameCert = askUser(gBundle.getString(msgNeedCertWantSame));
}
else {
if (askUser(gBundle.getString(msgNeedCertWantToSelect))) {
smimeSelectCert(pref);
}
}
}
else {
if (matchingOtherCert && (matchingOtherCert.dbKey == cert.dbKey)) {
userWantsSameCert = askUser(gBundle.getString(msgWantSame));
}
}
if (userWantsSameCert) {
otherCertInfo.value = cert.nickname + " [" + cert.serialNumber + "]";
otherCertInfo.nickname = cert.nickname;
otherCertInfo.dbKey = cert.dbKey;
enabler(true);
}
}
function smimeSelectCert(smime_cert)
{
var certInfo = document.getElementById(smime_cert);
if (!certInfo)
return;
var picker = Components.classes["@mozilla.org/user_cert_picker;1"]
.createInstance(Components.interfaces.nsIUserCertPicker);
var canceled = new Object;
var x509cert = 0;
var certUsage;
var selectEncryptionCert;
if (smime_cert == kEncryptionCertPref) {
selectEncryptionCert = true;
certUsage = email_recipient_cert_usage;
} else if (smime_cert == kSigningCertPref) {
selectEncryptionCert = false;
certUsage = email_signing_cert_usage;
}
try {
x509cert = picker.pickByUsage(window,
certInfo.value,
certUsage, // this is from enum SECCertUsage
false, true,
gIdentity.email,
canceled);
} catch(e) {
canceled.value = false;
x509cert = null;
}
if (!canceled.value) {
if (!x509cert) {
if (gIdentity.email) {
alertUser(gBundle.getFormattedString(selectEncryptionCert ?
"NoEncryptionCertForThisAddress" :
"NoSigningCertForThisAddress",
[ gIdentity.email ]));
} else {
alertUser(gBundle.getString(selectEncryptionCert ?
"NoEncryptionCert" : "NoSigningCert"));
}
}
else {
certInfo.removeAttribute("disabled");
certInfo.value = x509cert.nickname + " [" + x509cert.serialNumber + "]";
certInfo.nickname = x509cert.nickname;
certInfo.dbKey = x509cert.dbKey;
if (selectEncryptionCert) {
enableEncryptionControls(true);
checkOtherCert(x509cert,
kSigningCertPref, email_signing_cert_usage,
"signing_needCertWantSame",
"signing_wantSame",
"signing_needCertWantToSelect",
enableSigningControls);
} else {
enableSigningControls(true);
checkOtherCert(x509cert,
kEncryptionCertPref, email_recipient_cert_usage,
"encryption_needCertWantSame",
"encryption_wantSame",
"encryption_needCertWantToSelect",
enableEncryptionControls);
}
}
}
enableCertSelectButtons();
}
function enableEncryptionControls(do_enable)
{
if (gEncryptionChoicesLocked)
return;
if (do_enable) {
gEncryptAlways.removeAttribute("disabled");
gNeverEncrypt.removeAttribute("disabled");
gEncryptionCertName.removeAttribute("disabled");
}
else {
gEncryptAlways.setAttribute("disabled", "true");
gNeverEncrypt.setAttribute("disabled", "true");
gEncryptionCertName.setAttribute("disabled", "true");
gEncryptionChoices.value = 0;
}
}
function enableSigningControls(do_enable)
{
if (gSigningChoicesLocked)
return;
if (do_enable) {
gSignMessages.removeAttribute("disabled");
gSignCertName.removeAttribute("disabled");
}
else {
gSignMessages.setAttribute("disabled", "true");
gSignCertName.setAttribute("disabled", "true");
gSignMessages.checked = false;
}
}
function enableCertSelectButtons()
{
document.getElementById("signingCertSelectButton").removeAttribute("disabled");
if (document.getElementById('identity.signing_cert_name').value.length)
document.getElementById("signingCertClearButton").removeAttribute("disabled");
else
document.getElementById("signingCertClearButton").setAttribute("disabled", "true");
document.getElementById("encryptionCertSelectButton").removeAttribute("disabled");
if (document.getElementById('identity.encryption_cert_name').value.length)
document.getElementById("encryptionCertClearButton").removeAttribute("disabled");
else
document.getElementById("encryptionCertClearButton").setAttribute("disabled", "true");
}
function smimeClearCert(smime_cert)
{
var certInfo = document.getElementById(smime_cert);
if (!certInfo)
return;
certInfo.setAttribute("disabled", "true");
certInfo.value = "";
certInfo.nickname = "";
certInfo.dbKey = "";
if (smime_cert == kEncryptionCertPref) {
enableEncryptionControls(false);
} else if (smime_cert == kSigningCertPref) {
enableSigningControls(false);
}
enableCertSelectButtons();
}
function openCertManager()
{
// Check for an existing certManager window and focus it; it's not
// application modal.
let lastCertManager = Services.wm.getMostRecentWindow("mozilla:certmanager");
if (lastCertManager)
lastCertManager.focus();
else
window.openDialog("chrome://pippki/content/certManager.xul", "",
"centerscreen,resizable=yes,dialog=no");
}
function openDeviceManager()
{
// Check for an existing deviceManager window and focus it; it's not
// application modal.
let lastCertManager = Services.wm.getMostRecentWindow("mozilla:devicemanager");
if (lastCertManager)
lastCertManager.focus();
else
window.openDialog("chrome://pippki/content/device_manager.xul", "",
"centerscreen,resizable=yes,dialog=no");
}
function smimeOnLoadEditor()
{
smimeInitializeFields();
document.documentElement.setAttribute("ondialogaccept",
"return smimeOnAcceptEditor();");
}

View file

@ -0,0 +1,26 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://messenger/skin/accountManage.css" type="text/css"?>
<?xul-overlay href="chrome://messenger/content/am-smimeOverlay.xul"?>
<!DOCTYPE page SYSTEM "chrome://messenger/locale/am-smime.dtd">
<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
class="color-dialog"
onload="parent.onPanelLoaded('am-smime.xul');"
ondialogaccept="smimeOnAcceptEditor();">
<vbox flex="1" style="overflow: auto;">
<script type="application/javascript" src="chrome://messenger/content/AccountManager.js"/>
<script type="application/javascript" src="chrome://messenger/content/am-smime.js"/>
<dialogheader title="&securityTitle.label;"/>
<vbox flex="1" id="smimeEditing"/>
</vbox>
</page>

View file

@ -0,0 +1,39 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://messenger/skin/accountManage.css"
type="text/css"?>
<?xul-overlay href="chrome://messenger/content/am-smimeOverlay.xul"?>
<!DOCTYPE overlay SYSTEM "chrome://messenger/locale/am-smime.dtd">
<!--
This is the overlay that adds the SMIME configurator
to the identity editor of the account manager
-->
<overlay id="smimeAmIdEditOverlay"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript"
src="chrome://messenger/content/AccountManager.js"/>
<script type="application/javascript"
src="chrome://messenger/content/am-smime.js"/>
<tabs id="identitySettings">
<tab label="&securityTab.label;"/>
</tabs>
<tabpanels id="identityTabsPanels">
<vbox flex="1" name="smimeEditingContent" id="smimeEditing"/>
</tabpanels>
<script type="application/javascript">
<![CDATA[
window.addEventListener("load", smimeOnLoadEditor, false);
]]>
</script>
</overlay>

View file

@ -0,0 +1,102 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://messenger/skin/accountManage.css"
type="text/css"?>
<!DOCTYPE overlay SYSTEM "chrome://messenger/locale/am-smime.dtd">
<overlay xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<vbox id="smimeEditing">
<stringbundleset>
<stringbundle id="bundle_smime" src="chrome://messenger/locale/am-smime.properties"/>
<stringbundle id="bundle_brand" src="chrome://branding/locale/brand.properties"/>
</stringbundleset>
<label hidden="true" wsm_persist="true" id="identity.encryptionpolicy"/>
<description>&securityHeading.label;</description>
<groupbox id="signing.titlebox">
<caption label="&signingGroupTitle.label;"/>
<label value="&signingCert.message;" control="identity.signing_cert_name"
prefstring="mail.identity.%identitykey%.encryptionpolicy"/>
<hbox align="center">
<textbox id="identity.signing_cert_name" wsm_persist="true" flex="1"
prefstring="mail.identity.%identitykey%.signing_cert_name"
readonly="true" disabled="true"/>
<button id="signingCertSelectButton"
label="&digitalSign.certificate.button;"
accesskey="&digitalSign.certificate.accesskey;"
oncommand="smimeSelectCert('identity.signing_cert_name')"/>
<button id="signingCertClearButton"
label="&digitalSign.certificate_clear.button;"
accesskey="&digitalSign.certificate_clear.accesskey;"
oncommand="smimeClearCert('identity.signing_cert_name')"/>
</hbox>
<separator class="thin"/>
<checkbox id="identity.sign_mail" wsm_persist="true"
prefstring="mail.identity.%identitykey%.sign_mail"
label="&signMessage.label;" accesskey="&signMessage.accesskey;"/>
</groupbox>
<groupbox id="encryption.titlebox">
<caption label="&encryptionGroupTitle.label;"/>
<label value="&encryptionCert.message;"
control="identity.encryption_cert_name"/>
<hbox align="center">
<textbox id="identity.encryption_cert_name" wsm_persist="true" flex="1"
prefstring="mail.identity.%identitykey%.encryption_cert_name"
readonly="true" disabled="true"/>
<button id="encryptionCertSelectButton"
label="&encryption.certificate.button;"
accesskey="&encryption.certificate.accesskey;"
oncommand="smimeSelectCert('identity.encryption_cert_name')"/>
<button id="encryptionCertClearButton"
label="&encryption.certificate_clear.button;"
accesskey="&encryption.certificate_clear.accesskey;"
oncommand="smimeClearCert('identity.encryption_cert_name')"/>
</hbox>
<separator class="thin"/>
<label value="&encryptionChoiceLabel.label;" control="encryptionChoices"/>
<radiogroup id="encryptionChoices">
<radio id="encrypt_mail_never" wsm_persist="true" value="0"
label="&neverEncrypt.label;"
accesskey="&neverEncrypt.accesskey;"/>
<radio id="encrypt_mail_always" wsm_persist="true" value="2"
label="&alwaysEncryptMessage.label;"
accesskey="&alwaysEncryptMessage.accesskey;"/>
</radiogroup>
</groupbox>
<!-- Certificate manager -->
<groupbox id="smimeCertificateManager" orient="horizontal">
<caption label="&certificates.label;"/>
<button id="openCertManagerButton" oncommand="openCertManager();"
label="&manageCerts2.label;" accesskey="&manageCerts2.accesskey;"
prefstring="security.disable_button.openCertManager"/>
<button id="openDeviceManagerButton" oncommand="openDeviceManager();"
label="&manageDevices.label;" accesskey="&manageDevices.accesskey;"
prefstring="security.disable_button.openDeviceManager"/>
</groupbox>
</vbox>
</overlay>

View file

@ -0,0 +1,265 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* We expect the following arguments:
- pref name of LDAP directory to fetch from
- array with email addresses
Display modal dialog with message and stop button.
In onload, kick off binding to LDAP.
When bound, kick off the searches.
On finding certificates, import into permanent cert database.
When all searches are finished, close the dialog.
*/
Components.utils.import("resource://gre/modules/Services.jsm");
var nsIX509CertDB = Components.interfaces.nsIX509CertDB;
var nsX509CertDB = "@mozilla.org/security/x509certdb;1";
var CertAttribute = "usercertificate;binary";
var gEmailAddresses;
var gDirectoryPref;
var gLdapServerURL;
var gLdapConnection;
var gCertDB;
var gLdapOperation;
var gLogin;
function onLoad()
{
gDirectoryPref = window.arguments[0];
gEmailAddresses = window.arguments[1];
if (!gEmailAddresses.length)
{
window.close();
return;
}
setTimeout(search, 1);
}
function search()
{
// get the login to authenticate as, if there is one
try {
gLogin = Services.prefs.getComplexValue(gDirectoryPref + ".auth.dn", Components.interfaces.nsISupportsString).data;
} catch (ex) {
// if we don't have this pref, no big deal
}
try {
let url = Services.prefs.getCharPref(gDirectoryPref + ".uri");
gLdapServerURL = Services.io
.newURI(url, null, null).QueryInterface(Components.interfaces.nsILDAPURL);
gLdapConnection = Components.classes["@mozilla.org/network/ldap-connection;1"]
.createInstance().QueryInterface(Components.interfaces.nsILDAPConnection);
gLdapConnection.init(gLdapServerURL, gLogin, new boundListener(),
null, Components.interfaces.nsILDAPConnection.VERSION3);
} catch (ex) {
dump(ex);
dump(" exception creating ldap connection\n");
window.close();
}
}
function stopFetching()
{
if (gLdapOperation) {
try {
gLdapOperation.abandon();
}
catch (e) {
}
}
return true;
}
function importCert(ber_value)
{
if (!gCertDB) {
gCertDB = Components.classes[nsX509CertDB].getService(nsIX509CertDB);
}
var cert_length = new Object();
var cert_bytes = ber_value.get(cert_length);
if (cert_bytes) {
gCertDB.importEmailCertificate(cert_bytes, cert_length.value, null);
}
}
function getLDAPOperation()
{
gLdapOperation = Components.classes["@mozilla.org/network/ldap-operation;1"]
.createInstance().QueryInterface(Components.interfaces.nsILDAPOperation);
gLdapOperation.init(gLdapConnection,
new ldapMessageListener(),
null);
}
function getPassword()
{
// we only need a password if we are using credentials
if (gLogin)
{
let authPrompter = Services.ww.getNewAuthPrompter(window.QueryInterface(Components.interfaces.nsIDOMWindow));
let strBundle = document.getElementById('bundle_ldap');
let password = { value: "" };
// nsLDAPAutocompleteSession uses asciiHost instead of host for the prompt text, I think we should be
// consistent.
if (authPrompter.promptPassword(strBundle.getString("authPromptTitle"),
strBundle.getFormattedString("authPromptText", [gLdapServerURL.asciiHost]),
gLdapServerURL.spec,
authPrompter.SAVE_PASSWORD_PERMANENTLY,
password))
return password.value;
}
return null;
}
function kickOffBind()
{
try {
getLDAPOperation();
gLdapOperation.simpleBind(getPassword());
}
catch (e) {
window.close();
}
}
function kickOffSearch()
{
try {
var prefix1 = "";
var suffix1 = "";
var urlFilter = gLdapServerURL.filter;
if (urlFilter != null && urlFilter.length > 0 && urlFilter != "(objectclass=*)") {
if (urlFilter.startsWith('(')) {
prefix1 = "(&" + urlFilter;
}
else {
prefix1 = "(&(" + urlFilter + ")";
}
suffix1 = ")";
}
var prefix2 = "";
var suffix2 = "";
if (gEmailAddresses.length > 1) {
prefix2 = "(|";
suffix2 = ")";
}
var mailFilter = "";
for (var i = 0; i < gEmailAddresses.length; ++i) {
mailFilter += "(mail=" + gEmailAddresses[i] + ")";
}
var filter = prefix1 + prefix2 + mailFilter + suffix2 + suffix1;
var wanted_attributes = CertAttribute;
// Max search results =>
// Double number of email addresses, because each person might have
// multiple certificates listed. We expect at most two certificates,
// one for signing, one for encrypting.
// Maybe that number should be larger, to allow for deployments,
// where even more certs can be stored per user???
var maxEntriesWanted = gEmailAddresses.length * 2;
getLDAPOperation();
gLdapOperation.searchExt(gLdapServerURL.dn, gLdapServerURL.scope,
filter, wanted_attributes, 0, maxEntriesWanted);
}
catch (e) {
window.close();
}
}
function boundListener() {
}
boundListener.prototype.QueryInterface =
function(iid) {
if (iid.equals(Components.interfaces.nsISupports) ||
iid.equals(Components.interfaces.nsILDAPMessageListener))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
}
boundListener.prototype.onLDAPMessage =
function(aMessage) {
}
boundListener.prototype.onLDAPInit =
function(aConn, aStatus) {
kickOffBind();
}
function ldapMessageListener() {
}
ldapMessageListener.prototype.QueryInterface =
function(iid) {
if (iid.equals(Components.interfaces.nsISupports) ||
iid.equals(Components.interfaces.nsILDAPMessageListener))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
}
ldapMessageListener.prototype.onLDAPMessage =
function(aMessage) {
if (Components.interfaces.nsILDAPMessage.RES_SEARCH_RESULT == aMessage.type) {
window.close();
return;
}
if (Components.interfaces.nsILDAPMessage.RES_BIND == aMessage.type) {
if (Components.interfaces.nsILDAPErrors.SUCCESS != aMessage.errorCode) {
window.close();
}
else {
kickOffSearch();
}
return;
}
if (Components.interfaces.nsILDAPMessage.RES_SEARCH_ENTRY == aMessage.type) {
var outSize = new Object();
try {
var outBinValues = aMessage.getBinaryValues(CertAttribute, outSize);
var i;
for (i=0; i < outSize.value; ++i) {
importCert(outBinValues[i]);
}
}
catch (e) {
}
return;
}
}
ldapMessageListener.prototype.onLDAPInit =
function(aConn, aStatus) {
}

View file

@ -0,0 +1,24 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://messenger/skin/smime/certFetchingStatus.css" type="text/css"?>
<!DOCTYPE dialog SYSTEM "chrome://messenger-smime/locale/certFetchingStatus.dtd">
<dialog id="certFetchingStatus" title="&title.label;"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
style="width: 50em;"
buttons="cancel"
buttonlabelcancel="&stop.label;"
ondialogcancel="return stopFetching();"
onload="onLoad();">
<stringbundle id="bundle_ldap" src="chrome://mozldap/locale/ldap.properties"/>
<script type="application/javascript" src="chrome://messenger-smime/content/certFetchingStatus.js"/>
<description>&info.message;</description>
</dialog>

View file

@ -0,0 +1,73 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const nsIDialogParamBlock = Components.interfaces.nsIDialogParamBlock;
var dialogParams;
var itemCount = 0;
function onLoad()
{
dialogParams = window.arguments[0].QueryInterface(nsIDialogParamBlock);
var selectElement = document.getElementById("nicknames");
itemCount = dialogParams.GetInt(0);
var selIndex = dialogParams.GetInt(1);
if (selIndex < 0) {
selIndex = 0;
}
for (let i = 0; i < itemCount; i++) {
let menuItemNode = document.createElement("menuitem");
let nick = dialogParams.GetString(i);
menuItemNode.setAttribute("value", i);
menuItemNode.setAttribute("label", nick); // This is displayed.
selectElement.firstChild.appendChild(menuItemNode);
if (selIndex == i) {
selectElement.selectedItem = menuItemNode;
}
}
dialogParams.SetInt(0, 0); // Set cancel return value.
setDetails();
}
function setDetails()
{
let selItem = document.getElementById("nicknames").value;
if (selItem.length == 0) {
return;
}
let index = parseInt(selItem);
let details = dialogParams.GetString(index + itemCount);
document.getElementById("details").value = details;
}
function onCertSelected()
{
setDetails();
}
function doOK()
{
// Signal that the user accepted.
dialogParams.SetInt(0, 1);
// Signal the index of the selected cert in the list of cert nicknames
// provided.
let index = parseInt(document.getElementById("nicknames").value);
dialogParams.SetInt(1, index);
return true;
}
function doCancel()
{
dialogParams.SetInt(0, 0); // Signal that the user cancelled.
return true;
}

View file

@ -0,0 +1,38 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<!DOCTYPE dialog [
<!ENTITY % amSMIMEDTD SYSTEM "chrome://messenger/locale/am-smime.dtd" >
%amSMIMEDTD;
]>
<dialog id="certPicker" title="&certPicker.title;"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
style="width: 50em;"
buttons="accept,cancel"
ondialogaccept="return doOK();"
ondialogcancel="return doCancel();"
onload="onLoad();">
<script type="application/javascript"
src="chrome://messenger/content/certpicker.js"/>
<hbox align="center">
<broadcaster id="certSelected" oncommand="onCertSelected();"/>
<label id="pickerInfo" value="&certPicker.info;"/>
<!-- The items in this menulist must never be sorted,
but remain in the order filled by the application
-->
<menulist id="nicknames" observes="certSelected">
<menupopup/>
</menulist>
</hbox>
<separator class="thin"/>
<label value="&certPicker.detailsLabel;"/>
<textbox readonly="true" id="details" multiline="true"
style="height: 12em;" flex="1"/>
</dialog>

View file

@ -0,0 +1,357 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Components.utils.import("resource://gre/modules/Services.jsm");
// Account encryption policy values:
// const kEncryptionPolicy_Never = 0;
// 'IfPossible' was used by ns4.
// const kEncryptionPolicy_IfPossible = 1;
var kEncryptionPolicy_Always = 2;
var gEncryptedURIService =
Components.classes["@mozilla.org/messenger-smime/smime-encrypted-uris-service;1"]
.getService(Components.interfaces.nsIEncryptedSMIMEURIsService);
var gNextSecurityButtonCommand = "";
var gSMFields = null;
var gEncryptOptionChanged;
var gSignOptionChanged;
function onComposerLoad()
{
// Are we already set up ? Or are the required fields missing ?
if (gSMFields || !gMsgCompose || !gMsgCompose.compFields)
return;
gMsgCompose.compFields.securityInfo = null;
gSMFields = Components.classes["@mozilla.org/messenger-smime/composefields;1"]
.createInstance(Components.interfaces.nsIMsgSMIMECompFields);
if (!gSMFields)
return;
gMsgCompose.compFields.securityInfo = gSMFields;
// Set up the intial security state.
gSMFields.requireEncryptMessage =
gCurrentIdentity.getIntAttribute("encryptionpolicy") == kEncryptionPolicy_Always;
if (!gSMFields.requireEncryptMessage &&
gEncryptedURIService &&
gEncryptedURIService.isEncrypted(gMsgCompose.originalMsgURI))
{
// Override encryption setting if original is known as encrypted.
gSMFields.requireEncryptMessage = true;
}
if (gSMFields.requireEncryptMessage)
setEncryptionUI();
else
setNoEncryptionUI();
gSMFields.signMessage = gCurrentIdentity.getBoolAttribute("sign_mail");
if (gSMFields.signMessage)
setSignatureUI();
else
setNoSignatureUI();
}
addEventListener("load", smimeComposeOnLoad, {capture: false, once: true});
// this function gets called multiple times
function smimeComposeOnLoad()
{
onComposerLoad();
top.controllers.appendController(SecurityController);
addEventListener("compose-from-changed", onComposerFromChanged, true);
addEventListener("compose-send-message", onComposerSendMessage, true);
addEventListener("unload", smimeComposeOnUnload, {capture: false, once: true});
}
function smimeComposeOnUnload()
{
removeEventListener("compose-from-changed", onComposerFromChanged, true);
removeEventListener("compose-send-message", onComposerSendMessage, true);
top.controllers.removeController(SecurityController);
}
function showNeedSetupInfo()
{
let compSmimeBundle = document.getElementById("bundle_comp_smime");
let brandBundle = document.getElementById("bundle_brand");
if (!compSmimeBundle || !brandBundle)
return;
let buttonPressed = Services.prompt.confirmEx(window,
brandBundle.getString("brandShortName"),
compSmimeBundle.getString("NeedSetup"),
Services.prompt.STD_YES_NO_BUTTONS, 0, 0, 0, null, {});
if (buttonPressed == 0)
openHelp("sign-encrypt", "chrome://communicator/locale/help/suitehelp.rdf");
}
function toggleEncryptMessage()
{
if (!gSMFields)
return;
gSMFields.requireEncryptMessage = !gSMFields.requireEncryptMessage;
if (gSMFields.requireEncryptMessage)
{
// Make sure we have a cert.
if (!gCurrentIdentity.getUnicharAttribute("encryption_cert_name"))
{
gSMFields.requireEncryptMessage = false;
showNeedSetupInfo();
return;
}
setEncryptionUI();
}
else
{
setNoEncryptionUI();
}
gEncryptOptionChanged = true;
}
function toggleSignMessage()
{
if (!gSMFields)
return;
gSMFields.signMessage = !gSMFields.signMessage;
if (gSMFields.signMessage) // make sure we have a cert name...
{
if (!gCurrentIdentity.getUnicharAttribute("signing_cert_name"))
{
gSMFields.signMessage = false;
showNeedSetupInfo();
return;
}
setSignatureUI();
}
else
{
setNoSignatureUI();
}
gSignOptionChanged = true;
}
function setSecuritySettings(menu_id)
{
if (!gSMFields)
return;
document.getElementById("menu_securityEncryptRequire" + menu_id)
.setAttribute("checked", gSMFields.requireEncryptMessage);
document.getElementById("menu_securitySign" + menu_id)
.setAttribute("checked", gSMFields.signMessage);
}
function setNextCommand(what)
{
gNextSecurityButtonCommand = what;
}
function doSecurityButton()
{
var what = gNextSecurityButtonCommand;
gNextSecurityButtonCommand = "";
switch (what)
{
case "encryptMessage":
toggleEncryptMessage();
break;
case "signMessage":
toggleSignMessage();
break;
case "show":
default:
showMessageComposeSecurityStatus();
}
}
function setNoSignatureUI()
{
top.document.getElementById("securityStatus").removeAttribute("signing");
top.document.getElementById("signing-status").collapsed = true;
}
function setSignatureUI()
{
top.document.getElementById("securityStatus").setAttribute("signing", "ok");
top.document.getElementById("signing-status").collapsed = false;
}
function setNoEncryptionUI()
{
top.document.getElementById("securityStatus").removeAttribute("crypto");
top.document.getElementById("encryption-status").collapsed = true;
}
function setEncryptionUI()
{
top.document.getElementById("securityStatus").setAttribute("crypto", "ok");
top.document.getElementById("encryption-status").collapsed = false;
}
function showMessageComposeSecurityStatus()
{
Recipients2CompFields(gMsgCompose.compFields);
window.openDialog(
"chrome://messenger-smime/content/msgCompSecurityInfo.xul",
"",
"chrome,modal,resizable,centerscreen",
{
compFields : gMsgCompose.compFields,
subject : GetMsgSubjectElement().value,
smFields : gSMFields,
isSigningCertAvailable :
gCurrentIdentity.getUnicharAttribute("signing_cert_name") != "",
isEncryptionCertAvailable :
gCurrentIdentity.getUnicharAttribute("encryption_cert_name") != "",
currentIdentity : gCurrentIdentity
}
);
}
var SecurityController =
{
supportsCommand: function(command)
{
switch (command)
{
case "cmd_viewSecurityStatus":
return true;
default:
return false;
}
},
isCommandEnabled: function(command)
{
switch (command)
{
case "cmd_viewSecurityStatus":
return true;
default:
return false;
}
}
};
function onComposerSendMessage()
{
let missingCount = new Object();
let emailAddresses = new Object();
try
{
if (!gMsgCompose.compFields.securityInfo.requireEncryptMessage)
return;
Components.classes["@mozilla.org/messenger-smime/smimejshelper;1"]
.createInstance(Components.interfaces.nsISMimeJSHelper)
.getNoCertAddresses(gMsgCompose.compFields,
missingCount,
emailAddresses);
}
catch (e)
{
return;
}
if (missingCount.value > 0)
{
// The rules here: If the current identity has a directoryServer set, then
// use that, otherwise, try the global preference instead.
let autocompleteDirectory;
// Does the current identity override the global preference?
if (gCurrentIdentity.overrideGlobalPref)
{
autocompleteDirectory = gCurrentIdentity.directoryServer;
}
else
{
// Try the global one
if (Services.prefs.getBoolPref("ldap_2.autoComplete.useDirectory"))
autocompleteDirectory =
Services.prefs.getCharPref("ldap_2.autoComplete.directoryServer");
}
if (autocompleteDirectory)
window.openDialog("chrome://messenger-smime/content/certFetchingStatus.xul",
"",
"chrome,modal,resizable,centerscreen",
autocompleteDirectory,
emailAddresses.value);
}
}
function onComposerFromChanged()
{
if (!gSMFields)
return;
var encryptionPolicy = gCurrentIdentity.getIntAttribute("encryptionpolicy");
var useEncryption = false;
if (!gEncryptOptionChanged)
{
// Encryption wasn't manually checked.
// Set up the encryption policy from the setting of the new identity.
// 0 == never, 1 == if possible (ns4), 2 == always encrypt.
useEncryption = (encryptionPolicy == kEncryptionPolicy_Always);
}
else
{
useEncryption = !!gCurrentIdentity.getUnicharAttribute("encryption_cert_name");
}
gSMFields.requireEncryptMessage = useEncryption;
if (useEncryption)
setEncryptionUI();
else
setNoEncryptionUI();
// - If signing is disabled, we will not turn it on automatically.
// - If signing is enabled, but the new account defaults to not sign, we will turn signing off.
var signMessage = gCurrentIdentity.getBoolAttribute("sign_mail");
var useSigning = false;
if (!gSignOptionChanged)
{
// Signing wasn't manually checked.
// Set up the signing policy from the setting of the new identity.
useSigning = signMessage;
}
else
{
useSigning = !!gCurrentIdentity.getUnicharAttribute("signing_cert_name");
}
gSMFields.signMessage = useSigning;
if (useSigning)
setSignatureUI();
else
setNoSignatureUI();
}

View file

@ -0,0 +1,85 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://messenger/skin/smime/msgCompSMIMEOverlay.css" type="text/css"?>
<!DOCTYPE overlay SYSTEM "chrome://messenger-smime/locale/msgCompSMIMEOverlay.dtd">
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="chrome://messenger-smime/content/msgCompSMIMEOverlay.js"/>
<window id="msgcomposeWindow">
<broadcaster id="securityStatus" crypto="" signing=""/>
<observes element="securityStatus" attribute="crypto" />
<observes element="securityStatus" attribute="signing" />
<stringbundle id="bundle_comp_smime" src="chrome://messenger-smime/locale/msgCompSMIMEOverlay.properties"/>
<stringbundle id="bundle_brand" src="chrome://branding/locale/brand.properties"/>
</window>
<menupopup id="optionsMenuPopup"
onpopupshowing="setSecuritySettings(1);">
<menuseparator id="smimeOptionsSeparator"/>
<menuitem id="menu_securityEncryptRequire1"
type="checkbox"
label="&menu_securityEncryptRequire.label;"
accesskey="&menu_securityEncryptRequire.accesskey;"
oncommand="toggleEncryptMessage();"/>
<menuitem id="menu_securitySign1"
type="checkbox"
label="&menu_securitySign.label;"
accesskey="&menu_securitySign.accesskey;"
oncommand="toggleSignMessage();"/>
</menupopup>
<toolbarpalette id="MsgComposeToolbarPalette">
<toolbarbutton id="button-security"
type="menu-button"
class="toolbarbutton-1"
label="&securityButton.label;"
tooltiptext="&securityButton.tooltip;"
oncommand="doSecurityButton();">
<menupopup onpopupshowing="setSecuritySettings(2);">
<menuitem id="menu_securityEncryptRequire2"
type="checkbox"
label="&menu_securityEncryptRequire.label;"
accesskey="&menu_securityEncryptRequire.accesskey;"
oncommand="setNextCommand('encryptMessage');"/>
<menuitem id="menu_securitySign2"
type="checkbox"
label="&menu_securitySign.label;"
accesskey="&menu_securitySign.accesskey;"
oncommand="setNextCommand('signMessage');"/>
<menuseparator id="smimeToolbarButtonSeparator"/>
<menuitem id="menu_securityStatus2"
label="&menu_securityStatus.label;"
accesskey="&menu_securityStatus.accesskey;"
oncommand="setNextCommand('show');"/>
</menupopup>
</toolbarbutton>
</toolbarpalette>
<statusbar id="status-bar">
<statusbarpanel insertbefore="offline-status" class="statusbarpanel-iconic" collapsed="true"
id="signing-status" oncommand="showMessageComposeSecurityStatus();"/>
<statusbarpanel insertbefore="offline-status" class="statusbarpanel-iconic" collapsed="true"
id="encryption-status" oncommand="showMessageComposeSecurityStatus();"/>
</statusbar>
<commandset id="composeCommands">
<command id="cmd_viewSecurityStatus" oncommand="showMessageComposeSecurityStatus();"/>
</commandset>
<menupopup id="menu_View_Popup">
<menuseparator id="viewMenuBeforeSecurityStatusSeparator"/>
<menuitem id="menu_viewSecurityStatus"
label="&menu_viewSecurityStatus.label;"
accesskey="&menu_viewSecurityStatus.accesskey;"
command="cmd_viewSecurityStatus"/>
</menupopup>
</overlay>

View file

@ -0,0 +1,244 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Components.utils.import("resource://gre/modules/Services.jsm");
var gListBox;
var gViewButton;
var gBundle;
var gEmailAddresses;
var gCertStatusSummaries;
var gCertIssuedInfos;
var gCertExpiresInfos;
var gCerts;
var gCount;
var gSMimeContractID = "@mozilla.org/messenger-smime/smimejshelper;1";
var gISMimeJSHelper = Components.interfaces.nsISMimeJSHelper;
var gIX509Cert = Components.interfaces.nsIX509Cert;
var nsICertificateDialogs = Components.interfaces.nsICertificateDialogs;
var nsCertificateDialogs = "@mozilla.org/nsCertificateDialogs;1"
function getStatusExplanation(value)
{
switch (value)
{
case gIX509Cert.VERIFIED_OK:
return gBundle.getString("StatusValid");
case gIX509Cert.NOT_VERIFIED_UNKNOWN:
case gIX509Cert.INVALID_CA:
case gIX509Cert.USAGE_NOT_ALLOWED:
return gBundle.getString("StatusInvalid");
case gIX509Cert.CERT_REVOKED:
return gBundle.getString("StatusRevoked");
case gIX509Cert.CERT_EXPIRED:
return gBundle.getString("StatusExpired");
case gIX509Cert.CERT_NOT_TRUSTED:
case gIX509Cert.ISSUER_NOT_TRUSTED:
case gIX509Cert.ISSUER_UNKNOWN:
return gBundle.getString("StatusUntrusted");
}
return "";
}
function onLoad()
{
var params = window.arguments[0];
if (!params)
return;
var helper = Components.classes[gSMimeContractID].createInstance(gISMimeJSHelper);
if (!helper)
return;
gListBox = document.getElementById("infolist");
gViewButton = document.getElementById("viewCertButton");
gBundle = document.getElementById("bundle_smime_comp_info");
gEmailAddresses = new Object();
gCertStatusSummaries = new Object();
gCertIssuedInfos = new Object();
gCertExpiresInfos = new Object();
gCerts = new Object();
gCount = new Object();
var canEncrypt = new Object();
var allow_ldap_cert_fetching = false;
try {
if (params.compFields.securityInfo.requireEncryptMessage) {
allow_ldap_cert_fetching = true;
}
}
catch (e)
{
}
while (true)
{
try
{
helper.getRecipientCertsInfo(
params.compFields,
gCount,
gEmailAddresses,
gCertStatusSummaries,
gCertIssuedInfos,
gCertExpiresInfos,
gCerts,
canEncrypt);
}
catch (e)
{
dump(e);
return;
}
if (!allow_ldap_cert_fetching)
break;
allow_ldap_cert_fetching = false;
var missing = new Array();
for (var j = gCount.value - 1; j >= 0; --j)
{
if (!gCerts.value[j])
{
missing[missing.length] = gEmailAddresses.value[j];
}
}
if (missing.length > 0)
{
var autocompleteLdap = Services.prefs
.getBoolPref("ldap_2.autoComplete.useDirectory");
if (autocompleteLdap)
{
var autocompleteDirectory = null;
if (params.currentIdentity.overrideGlobalPref) {
autocompleteDirectory = params.currentIdentity.directoryServer;
} else {
autocompleteDirectory = Services.prefs
.getCharPref("ldap_2.autoComplete.directoryServer");
}
if (autocompleteDirectory)
{
window.openDialog('chrome://messenger-smime/content/certFetchingStatus.xul',
'',
'chrome,resizable=1,modal=1,dialog=1',
autocompleteDirectory,
missing
);
}
}
}
}
if (gBundle)
{
var yes_string = gBundle.getString("StatusYes");
var no_string = gBundle.getString("StatusNo");
var not_possible_string = gBundle.getString("StatusNotPossible");
var signed_element = document.getElementById("signed");
var encrypted_element = document.getElementById("encrypted");
if (params.smFields.requireEncryptMessage)
{
if (params.isEncryptionCertAvailable && canEncrypt.value)
{
encrypted_element.value = yes_string;
}
else
{
encrypted_element.value = not_possible_string;
}
}
else
{
encrypted_element.value = no_string;
}
if (params.smFields.signMessage)
{
if (params.isSigningCertAvailable)
{
signed_element.value = yes_string;
}
else
{
signed_element.value = not_possible_string;
}
}
else
{
signed_element.value = no_string;
}
}
var imax = gCount.value;
for (var i = 0; i < imax; ++i)
{
var listitem = document.createElement("listitem");
listitem.appendChild(createCell(gEmailAddresses.value[i]));
if (!gCerts.value[i])
{
listitem.appendChild(createCell(gBundle.getString("StatusNotFound")));
}
else
{
listitem.appendChild(createCell(getStatusExplanation(gCertStatusSummaries.value[i])));
listitem.appendChild(createCell(gCertIssuedInfos.value[i]));
listitem.appendChild(createCell(gCertExpiresInfos.value[i]));
}
gListBox.appendChild(listitem);
}
}
function onSelectionChange(event)
{
gViewButton.disabled = !(gListBox.selectedItems.length == 1 &&
certForRow(gListBox.selectedIndex));
}
function viewCertHelper(parent, cert) {
var cd = Components.classes[nsCertificateDialogs].getService(nsICertificateDialogs);
cd.viewCert(parent, cert);
}
function certForRow(aRowIndex) {
return gCerts.value[aRowIndex];
}
function viewSelectedCert()
{
if (!gViewButton.disabled)
viewCertHelper(window, certForRow(gListBox.selectedIndex));
}
function doHelpButton()
{
openHelp('compose_security', 'chrome://communicator/locale/help/suitehelp.rdf');
}
function createCell(label)
{
var cell = document.createElement("listcell");
cell.setAttribute("label", label)
return cell;
}

View file

@ -0,0 +1,68 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://messenger/skin/smime/msgCompSecurityInfo.css" type="text/css"?>
<!DOCTYPE dialog SYSTEM "chrome://messenger-smime/locale/msgCompSecurityInfo.dtd">
<dialog id="msgCompSecurityInfo" title="&title.label;"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
style="width: 50em;"
persist="width height"
buttons="accept"
onload="onLoad();">
<script type="application/javascript" src="chrome://messenger-smime/content/msgCompSecurityInfo.js"/>
<stringbundle id="bundle_smime_comp_info" src="chrome://messenger-smime/locale/msgCompSecurityInfo.properties"/>
<description>&subject.plaintextWarning;</description>
<separator class="thin"/>
<description>&status.heading;</description>
<grid>
<columns>
<column/>
<column/>
</columns>
<rows>
<row>
<label value="&status.signed;"/>
<label id="signed"/>
</row>
<row>
<label value="&status.encrypted;"/>
<label id="encrypted"/>
</row>
</rows>
</grid>
<separator class="thin"/>
<label value="&status.certificates;" control="infolist"/>
<listbox id="infolist" flex="1"
onselect="onSelectionChange(event);">
<listcols>
<listcol flex="3" width="0"/>
<splitter class="tree-splitter"/>
<listcol flex="1" width="0"/>
<splitter class="tree-splitter"/>
<listcol flex="2" width="0"/>
<splitter class="tree-splitter"/>
<listcol flex="2" width="0"/>
</listcols>
<listhead>
<listheader label="&tree.recipient;"/>
<listheader label="&tree.status;"/>
<listheader label="&tree.issuedDate;"/>
<listheader label="&tree.expiresDate;"/>
</listhead>
</listbox>
<hbox pack="start">
<button id="viewCertButton" disabled="true"
label="&view.label;" accesskey="&view.accesskey;"
oncommand="viewSelectedCert();"/>
</hbox>
</dialog>

View file

@ -0,0 +1,264 @@
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var gSignedUINode = null;
var gEncryptedUINode = null;
var gSMIMEContainer = null;
var gStatusBar = null;
var gSignedStatusPanel = null;
var gEncryptedStatusPanel = null;
var gEncryptedURIService = null;
var gMyLastEncryptedURI = null;
var gSMIMEBundle = null;
// var gBrandBundle; -- defined in mailWindow.js
// manipulates some globals from msgReadSMIMEOverlay.js
var nsICMSMessageErrors = Components.interfaces.nsICMSMessageErrors;
/// Get the necko URL for the message URI.
function neckoURLForMessageURI(aMessageURI)
{
let msgSvc = Components.classes["@mozilla.org/messenger;1"]
.createInstance(Components.interfaces.nsIMessenger)
.messageServiceFromURI(aMessageURI);
let neckoURI = {};
msgSvc.GetUrlForUri(aMessageURI, neckoURI, null);
return neckoURI.value.spec;
}
var smimeHeaderSink =
{
maxWantedNesting: function()
{
return 1;
},
signedStatus: function(aNestingLevel, aSignatureStatus, aSignerCert)
{
if (aNestingLevel > 1) {
// we are not interested
return;
}
gSignatureStatus = aSignatureStatus;
gSignerCert = aSignerCert;
gSMIMEContainer.collapsed = false;
gSignedUINode.collapsed = false;
gSignedStatusPanel.collapsed = false;
switch (aSignatureStatus) {
case nsICMSMessageErrors.SUCCESS:
gSignedUINode.setAttribute("signed", "ok");
gStatusBar.setAttribute("signed", "ok");
break;
case nsICMSMessageErrors.VERIFY_NOT_YET_ATTEMPTED:
gSignedUINode.setAttribute("signed", "unknown");
gStatusBar.setAttribute("signed", "unknown");
break;
case nsICMSMessageErrors.VERIFY_CERT_WITHOUT_ADDRESS:
case nsICMSMessageErrors.VERIFY_HEADER_MISMATCH:
gSignedUINode.setAttribute("signed", "mismatch");
gStatusBar.setAttribute("signed", "mismatch");
break;
default:
gSignedUINode.setAttribute("signed", "notok");
gStatusBar.setAttribute("signed", "notok");
break;
}
},
encryptionStatus: function(aNestingLevel, aEncryptionStatus, aRecipientCert)
{
if (aNestingLevel > 1) {
// we are not interested
return;
}
gEncryptionStatus = aEncryptionStatus;
gEncryptionCert = aRecipientCert;
gSMIMEContainer.collapsed = false;
gEncryptedUINode.collapsed = false;
gEncryptedStatusPanel.collapsed = false;
if (nsICMSMessageErrors.SUCCESS == aEncryptionStatus)
{
gEncryptedUINode.setAttribute("encrypted", "ok");
gStatusBar.setAttribute("encrypted", "ok");
}
else
{
gEncryptedUINode.setAttribute("encrypted", "notok");
gStatusBar.setAttribute("encrypted", "notok");
}
if (gEncryptedURIService)
{
// Remember the message URI and the corresponding necko URI.
gMyLastEncryptedURI = GetLoadedMessage();
gEncryptedURIService.rememberEncrypted(gMyLastEncryptedURI);
gEncryptedURIService.rememberEncrypted(
neckoURLForMessageURI(gMyLastEncryptedURI));
}
switch (aEncryptionStatus)
{
case nsICMSMessageErrors.SUCCESS:
case nsICMSMessageErrors.ENCRYPT_INCOMPLETE:
break;
default:
var brand = gBrandBundle.getString("brandShortName");
var title = gSMIMEBundle.getString("CantDecryptTitle").replace(/%brand%/g, brand);
var body = gSMIMEBundle.getString("CantDecryptBody").replace(/%brand%/g, brand);
// insert our message
msgWindow.displayHTMLInMessagePane(title,
"<html>\n" +
"<body bgcolor=\"#fafaee\">\n" +
"<center><br><br><br>\n" +
"<table>\n" +
"<tr><td>\n" +
"<center><strong><font size=\"+3\">\n" +
title+"</font></center><br>\n" +
body+"\n" +
"</td></tr></table></center></body></html>", false);
}
},
QueryInterface : function(iid)
{
if (iid.equals(Components.interfaces.nsIMsgSMIMEHeaderSink) || iid.equals(Components.interfaces.nsISupports))
return this;
throw Components.results.NS_NOINTERFACE;
}
};
function forgetEncryptedURI()
{
if (gMyLastEncryptedURI && gEncryptedURIService)
{
gEncryptedURIService.forgetEncrypted(gMyLastEncryptedURI);
gEncryptedURIService.forgetEncrypted(
neckoURLForMessageURI(gMyLastEncryptedURI));
gMyLastEncryptedURI = null;
}
}
function onSMIMEStartHeaders()
{
gEncryptionStatus = -1;
gSignatureStatus = -1;
gSignerCert = null;
gEncryptionCert = null;
gSMIMEContainer.collapsed = true;
gSignedUINode.collapsed = true;
gSignedUINode.removeAttribute("signed");
gSignedStatusPanel.collapsed = true;
gStatusBar.removeAttribute("signed");
gEncryptedUINode.collapsed = true;
gEncryptedUINode.removeAttribute("encrypted");
gEncryptedStatusPanel.collapsed = true;
gStatusBar.removeAttribute("encrypted");
forgetEncryptedURI();
}
function onSMIMEEndHeaders()
{}
function onSmartCardChange()
{
// only reload encrypted windows
if (gMyLastEncryptedURI && gEncryptionStatus != -1)
ReloadMessage();
}
function msgHdrViewSMIMEOnLoad(event)
{
window.crypto.enableSmartCardEvents = true;
document.addEventListener("smartcard-insert", onSmartCardChange, false);
document.addEventListener("smartcard-remove", onSmartCardChange, false);
if (!gSMIMEBundle)
gSMIMEBundle = document.getElementById("bundle_read_smime");
// we want to register our security header sink as an opaque nsISupports
// on the msgHdrSink used by mail.....
msgWindow.msgHeaderSink.securityInfo = smimeHeaderSink;
gSignedUINode = document.getElementById('signedHdrIcon');
gEncryptedUINode = document.getElementById('encryptedHdrIcon');
gSMIMEContainer = document.getElementById('smimeBox');
gStatusBar = document.getElementById('status-bar');
gSignedStatusPanel = document.getElementById('signed-status');
gEncryptedStatusPanel = document.getElementById('encrypted-status');
// add ourself to the list of message display listeners so we get notified when we are about to display a
// message.
var listener = {};
listener.onStartHeaders = onSMIMEStartHeaders;
listener.onEndHeaders = onSMIMEEndHeaders;
gMessageListeners.push(listener);
gEncryptedURIService =
Components.classes["@mozilla.org/messenger-smime/smime-encrypted-uris-service;1"]
.getService(Components.interfaces.nsIEncryptedSMIMEURIsService);
}
function msgHdrViewSMIMEOnUnload(event)
{
window.crypto.enableSmartCardEvents = false;
document.removeEventListener("smartcard-insert", onSmartCardChange, false);
document.removeEventListener("smartcard-remove", onSmartCardChange, false);
forgetEncryptedURI();
removeEventListener("messagepane-loaded", msgHdrViewSMIMEOnLoad, true);
removeEventListener("messagepane-unloaded", msgHdrViewSMIMEOnUnload, true);
removeEventListener("messagepane-hide", msgHdrViewSMIMEOnMessagePaneHide, true);
removeEventListener("messagepane-unhide", msgHdrViewSMIMEOnMessagePaneUnhide, true);
}
function msgHdrViewSMIMEOnMessagePaneHide()
{
gSMIMEContainer.collapsed = true;
gSignedUINode.collapsed = true;
gSignedStatusPanel.collapsed = true;
gEncryptedUINode.collapsed = true;
gEncryptedStatusPanel.collapsed = true;
}
function msgHdrViewSMIMEOnMessagePaneUnhide()
{
if (gEncryptionStatus != -1 || gSignatureStatus != -1)
{
gSMIMEContainer.collapsed = false;
if (gSignatureStatus != -1)
{
gSignedUINode.collapsed = false;
gSignedStatusPanel.collapsed = false;
}
if (gEncryptionStatus != -1)
{
gEncryptedUINode.collapsed = false;
gEncryptedStatusPanel.collapsed = false;
}
}
}
addEventListener('messagepane-loaded', msgHdrViewSMIMEOnLoad, true);
addEventListener('messagepane-unloaded', msgHdrViewSMIMEOnUnload, true);
addEventListener('messagepane-hide', msgHdrViewSMIMEOnMessagePaneHide, true);
addEventListener('messagepane-unhide', msgHdrViewSMIMEOnMessagePaneUnhide, true);

View file

@ -0,0 +1,29 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://messenger/skin/smime/msgHdrViewSMIMEOverlay.css" type="text/css"?>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="chrome://messenger-smime/content/msgHdrViewSMIMEOverlay.js"/>
<!-- These stringbundles are already defined in msgReadSMIMEOverlay.xul!
<stringbundleset id="stringbundleset">
<stringbundle id="bundle_read_smime" src="chrome://messenger-smime/locale/msgReadSMIMEOverlay.properties"/>
<stringbundle id="bundle_brand" src="chrome://branding/locale/brand.properties"/>
</stringbundleset>
-->
<hbox id="expandedHeaderView">
<vbox id="smimeBox" insertafter="expandedHeaders" collapsed="true">
<spacer flex="1"/>
<image id="signedHdrIcon"
onclick="showMessageReadSecurityInfo();" collapsed="true"/>
<image id="encryptedHdrIcon"
onclick="showMessageReadSecurityInfo();" collapsed="true"/>
<spacer flex="1"/>
</vbox>
</hbox>
</overlay>

View file

@ -0,0 +1,102 @@
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Components.utils.import("resource://gre/modules/Services.jsm");
var gEncryptionStatus = -1;
var gSignatureStatus = -1;
var gSignerCert = null;
var gEncryptionCert = null;
addEventListener("load", smimeReadOnLoad, {capture: false, once: true});
function smimeReadOnLoad()
{
top.controllers.appendController(SecurityController);
addEventListener("unload", smimeReadOnUnload, {capture: false, once: true});
}
function smimeReadOnUnload()
{
top.controllers.removeController(SecurityController);
}
function showImapSignatureUnknown()
{
let readSmimeBundle = document.getElementById("bundle_read_smime");
let brandBundle = document.getElementById("bundle_brand");
if (!readSmimeBundle || !brandBundle)
return;
if (Services.prompt.confirm(window, brandBundle.getString("brandShortName"),
readSmimeBundle.getString("ImapOnDemand")))
{
gDBView.reloadMessageWithAllParts();
}
}
function showMessageReadSecurityInfo()
{
let gSignedUINode = document.getElementById("signedHdrIcon");
if (gSignedUINode && gSignedUINode.getAttribute("signed") == "unknown")
{
showImapSignatureUnknown();
return;
}
let params = Components.classes["@mozilla.org/embedcomp/dialogparam;1"]
.createInstance(Components.interfaces.nsIDialogParamBlock);
params.objects = Components.classes["@mozilla.org/array;1"]
.createInstance(Components.interfaces.nsIMutableArray);
// Append even if null... the receiver must handle that.
params.objects.appendElement(gSignerCert, false);
params.objects.appendElement(gEncryptionCert, false);
// int array starts with index 0, but that is used for window exit status
params.SetInt(1, gSignatureStatus);
params.SetInt(2, gEncryptionStatus);
window.openDialog("chrome://messenger-smime/content/msgReadSecurityInfo.xul",
"", "chrome,resizable,modal,dialog,centerscreen", params);
}
var SecurityController =
{
supportsCommand: function(command)
{
switch (command)
{
case "cmd_viewSecurityStatus":
return true;
default:
return false;
}
},
isCommandEnabled: function(command)
{
switch (command)
{
case "cmd_viewSecurityStatus":
if (document.documentElement.getAttribute('windowtype') == "mail:messageWindow")
return GetNumSelectedMessages() > 0;
if (GetNumSelectedMessages() > 0 && gDBView)
{
let enabled = {value: false};
let checkStatus = {};
gDBView.getCommandStatus(nsMsgViewCommandType.cmdRequiringMsgBody,
enabled, checkStatus);
return enabled.value;
}
// else: fall through.
default:
return false;
}
}
};

View file

@ -0,0 +1,34 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://messenger/skin/smime/msgReadSMIMEOverlay.css" type="text/css"?>
<!DOCTYPE overlay SYSTEM "chrome://messenger-smime/locale/msgReadSMIMEOverlay.dtd">
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="chrome://messenger-smime/content/msgReadSMIMEOverlay.js"/>
<commandset id="mailViewMenuItems">
<command id="cmd_viewSecurityStatus" oncommand="showMessageReadSecurityInfo();" disabled="true"/>
</commandset>
<menupopup id="menu_View_Popup">
<menuitem insertafter="pageSourceMenuItem" label="&menu_securityStatus.label;"
accesskey="&menu_securityStatus.accesskey;" command="cmd_viewSecurityStatus"/>
</menupopup>
<statusbar id="status-bar">
<statusbarpanel insertbefore="offline-status" class="statusbarpanel-iconic"
id="signed-status" collapsed="true" oncommand="showMessageReadSecurityInfo();"/>
<statusbarpanel insertbefore="offline-status" class="statusbarpanel-iconic"
id="encrypted-status" collapsed="true" oncommand="showMessageReadSecurityInfo();"/>
<stringbundle id="bundle_read_smime" src="chrome://messenger-smime/locale/msgReadSMIMEOverlay.properties"/>
<!-- This stringbundle is already defined on top window level!
<stringbundle id="bundle_brand" src="chrome://branding/locale/brand.properties"/>
-->
</statusbar>
</overlay>

View file

@ -0,0 +1,232 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var nsIDialogParamBlock = Components.interfaces.nsIDialogParamBlock;
var nsIX509Cert = Components.interfaces.nsIX509Cert;
var nsICMSMessageErrors = Components.interfaces.nsICMSMessageErrors;
var nsICertificateDialogs = Components.interfaces.nsICertificateDialogs;
var nsCertificateDialogs = "@mozilla.org/nsCertificateDialogs;1"
var gSignerCert = null;
var gEncryptionCert = null;
var gSignatureStatus = -1;
var gEncryptionStatus = -1;
function setText(id, value) {
var element = document.getElementById(id);
if (!element)
return;
if (element.hasChildNodes())
element.firstChild.remove();
var textNode = document.createTextNode(value);
element.appendChild(textNode);
}
function onLoad()
{
var paramBlock = window.arguments[0].QueryInterface(nsIDialogParamBlock);
paramBlock.objects.QueryInterface(Components.interfaces.nsIMutableArray);
try {
gSignerCert = paramBlock.objects.queryElementAt(0, nsIX509Cert);
} catch(e) { } // maybe null
try {
gEncryptionCert = paramBlock.objects.queryElementAt(1, nsIX509Cert);
} catch(e) { } // maybe null
gSignatureStatus = paramBlock.GetInt(1);
gEncryptionStatus = paramBlock.GetInt(2);
var bundle = document.getElementById("bundle_smime_read_info");
if (bundle) {
var sigInfoLabel = null;
var sigInfoHeader = null;
var sigInfo = null;
var sigInfo_clueless = false;
switch (gSignatureStatus) {
case -1:
case nsICMSMessageErrors.VERIFY_NOT_SIGNED:
sigInfoLabel = "SINoneLabel";
sigInfo = "SINone";
break;
case nsICMSMessageErrors.SUCCESS:
sigInfoLabel = "SIValidLabel";
sigInfo = "SIValid";
break;
case nsICMSMessageErrors.VERIFY_BAD_SIGNATURE:
case nsICMSMessageErrors.VERIFY_DIGEST_MISMATCH:
sigInfoLabel = "SIInvalidLabel";
sigInfoHeader = "SIInvalidHeader";
sigInfo = "SIContentAltered";
break;
case nsICMSMessageErrors.VERIFY_UNKNOWN_ALGO:
case nsICMSMessageErrors.VERIFY_UNSUPPORTED_ALGO:
sigInfoLabel = "SIInvalidLabel";
sigInfoHeader = "SIInvalidHeader";
sigInfo = "SIInvalidCipher";
break;
case nsICMSMessageErrors.VERIFY_HEADER_MISMATCH:
sigInfoLabel = "SIPartiallyValidLabel";
sigInfoHeader = "SIPartiallyValidHeader";
sigInfo = "SIHeaderMismatch";
break;
case nsICMSMessageErrors.VERIFY_CERT_WITHOUT_ADDRESS:
sigInfoLabel = "SIPartiallyValidLabel";
sigInfoHeader = "SIPartiallyValidHeader";
sigInfo = "SICertWithoutAddress";
break;
case nsICMSMessageErrors.VERIFY_UNTRUSTED:
sigInfoLabel = "SIInvalidLabel";
sigInfoHeader = "SIInvalidHeader";
sigInfo = "SIUntrustedCA";
// XXX Need to extend to communicate better errors
// might also be:
// SIExpired SIRevoked SINotYetValid SIUnknownCA SIExpiredCA SIRevokedCA SINotYetValidCA
break;
case nsICMSMessageErrors.VERIFY_NOT_YET_ATTEMPTED:
case nsICMSMessageErrors.GENERAL_ERROR:
case nsICMSMessageErrors.VERIFY_NO_CONTENT_INFO:
case nsICMSMessageErrors.VERIFY_BAD_DIGEST:
case nsICMSMessageErrors.VERIFY_NOCERT:
case nsICMSMessageErrors.VERIFY_ERROR_UNVERIFIED:
case nsICMSMessageErrors.VERIFY_ERROR_PROCESSING:
case nsICMSMessageErrors.VERIFY_MALFORMED_SIGNATURE:
sigInfoLabel = "SIInvalidLabel";
sigInfoHeader = "SIInvalidHeader";
sigInfo_clueless = true;
break;
default:
Components.utils.reportError("Unexpected gSignatureStatus: " +
gSignatureStatus);
}
document.getElementById("signatureLabel").value =
bundle.getString(sigInfoLabel);
var label;
if (sigInfoHeader) {
label = document.getElementById("signatureHeader");
label.collapsed = false;
label.value = bundle.getString(sigInfoHeader);
}
var str;
if (sigInfo) {
str = bundle.getString(sigInfo);
}
else if (sigInfo_clueless) {
str = bundle.getString("SIClueless") + " (" + gSignatureStatus + ")";
}
setText("signatureExplanation", str);
var encInfoLabel = null;
var encInfoHeader = null;
var encInfo = null;
var encInfo_clueless = false;
switch (gEncryptionStatus) {
case -1:
encInfoLabel = "EINoneLabel2";
encInfo = "EINone";
break;
case nsICMSMessageErrors.SUCCESS:
encInfoLabel = "EIValidLabel";
encInfo = "EIValid";
break;
case nsICMSMessageErrors.ENCRYPT_INCOMPLETE:
encInfoLabel = "EIInvalidLabel";
encInfo = "EIContentAltered";
break;
case nsICMSMessageErrors.GENERAL_ERROR:
encInfoLabel = "EIInvalidLabel";
encInfoHeader = "EIInvalidHeader";
encInfo_clueless = 1;
break;
default:
Components.utils.reportError("Unexpected gEncryptionStatus: " +
gEncryptionStatus);
}
document.getElementById("encryptionLabel").value =
bundle.getString(encInfoLabel);
if (encInfoHeader) {
label = document.getElementById("encryptionHeader");
label.collapsed = false;
label.value = bundle.getString(encInfoHeader);
}
if (encInfo) {
str = bundle.getString(encInfo);
}
else if (encInfo_clueless) {
str = bundle.getString("EIClueless");
}
setText("encryptionExplanation", str);
}
if (gSignerCert) {
document.getElementById("signatureCert").collapsed = false;
if (gSignerCert.subjectName) {
document.getElementById("signedBy").value = gSignerCert.commonName;
}
if (gSignerCert.emailAddress) {
document.getElementById("signerEmail").value = gSignerCert.emailAddress;
}
if (gSignerCert.issuerName) {
document.getElementById("sigCertIssuedBy").value = gSignerCert.issuerCommonName;
}
}
if (gEncryptionCert) {
document.getElementById("encryptionCert").collapsed = false;
if (gEncryptionCert.subjectName) {
document.getElementById("encryptedFor").value = gEncryptionCert.commonName;
}
if (gEncryptionCert.emailAddress) {
document.getElementById("recipientEmail").value = gEncryptionCert.emailAddress;
}
if (gEncryptionCert.issuerName) {
document.getElementById("encCertIssuedBy").value = gEncryptionCert.issuerCommonName;
}
}
}
function viewCertHelper(parent, cert) {
var cd = Components.classes[nsCertificateDialogs].getService(nsICertificateDialogs);
cd.viewCert(parent, cert);
}
function viewSignatureCert()
{
if (gSignerCert) {
viewCertHelper(window, gSignerCert);
}
}
function viewEncryptionCert()
{
if (gEncryptionCert) {
viewCertHelper(window, gEncryptionCert);
}
}
function doHelpButton()
{
openHelp('received_security');
}

View file

@ -0,0 +1,68 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://messenger/skin/smime/msgReadSecurityInfo.css" type="text/css"?>
<!DOCTYPE dialog SYSTEM "chrome://messenger-smime/locale/msgReadSecurityInfo.dtd">
<dialog id="msgReadSecurityInfo" title="&status.label;"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
style="width: 40em;"
buttons="accept"
onload="onLoad();">
<script type="application/javascript" src="chrome://messenger-smime/content/msgReadSecurityInfo.js"/>
<stringbundle id="bundle_smime_read_info" src="chrome://messenger-smime/locale/msgSecurityInfo.properties"/>
<vbox flex="1">
<label id="signatureLabel"/>
<label id="signatureHeader" collapsed="true"/>
<description id="signatureExplanation"/>
<vbox id="signatureCert" collapsed="true">
<hbox>
<label id="signedByLabel">&signer.name;</label>
<description id="signedBy"/>
</hbox>
<hbox>
<label id="signerEmailLabel">&email.address;</label>
<description id="signerEmail"/>
</hbox>
<hbox>
<label id="sigCertIssuedByLabel">&issuer.name;</label>
<description id="sigCertIssuedBy"/>
</hbox>
<hbox>
<button id="signatureCertView" label="&signatureCert.label;"
oncommand="viewSignatureCert()"/>
</hbox>
</vbox>
<separator/>
<label id="encryptionLabel"/>
<label id="encryptionHeader" collapsed="true"/>
<description id="encryptionExplanation"/>
<vbox id="encryptionCert" collapsed="true">
<hbox>
<label id="encryptedForLabel">&recipient.name;</label>
<description id="encryptedFor"/>
</hbox>
<hbox>
<label id="recipientEmailLabel">&email.address;</label>
<description id="recipientEmail"/>
</hbox>
<hbox>
<label id="encCertIssuedByLabel">&issuer.name;</label>
<description id="encCertIssuedBy"/>
</hbox>
<hbox>
<button id="encryptionCertView" label="&encryptionCert.label;"
oncommand="viewEncryptionCert()"/>
</hbox>
</vbox>
</vbox>
</dialog>

View file

@ -0,0 +1,14 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
Add any default pref values we want for smime
*/
pref("mail.identity.default.encryption_cert_name","");
pref("mail.identity.default.encryptionpolicy", 0);
pref("mail.identity.default.signing_cert_name", "");
pref("mail.identity.default.sign_mail", false);

View file

@ -0,0 +1,30 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifdef MOZ_SUITE
messenger.jar:
% content messenger-smime %content/messenger-smime/
% overlay chrome://messenger/content/messengercompose/messengercompose.xul chrome://messenger-smime/content/msgCompSMIMEOverlay.xul
% overlay chrome://messenger/content/msgHdrViewOverlay.xul chrome://messenger-smime/content/msgHdrViewSMIMEOverlay.xul
% overlay chrome://messenger/content/mailWindowOverlay.xul chrome://messenger-smime/content/msgReadSMIMEOverlay.xul
% overlay chrome://messenger/content/am-identity-edit.xul chrome://messenger/content/am-smimeIdentityEditOverlay.xul
content/messenger/am-smime.xul (content/am-smime.xul)
content/messenger/am-smime.js (content/am-smime.js)
content/messenger/am-smimeIdentityEditOverlay.xul (content/am-smimeIdentityEditOverlay.xul)
content/messenger/am-smimeOverlay.xul (content/am-smimeOverlay.xul)
content/messenger/certpicker.js (content/certpicker.js)
content/messenger/certpicker.xul (content/certpicker.xul)
content/messenger-smime/msgCompSMIMEOverlay.js (content/msgCompSMIMEOverlay.js)
content/messenger-smime/msgCompSMIMEOverlay.xul (content/msgCompSMIMEOverlay.xul)
content/messenger-smime/msgReadSMIMEOverlay.js (content/msgReadSMIMEOverlay.js)
content/messenger-smime/msgReadSMIMEOverlay.xul (content/msgReadSMIMEOverlay.xul)
content/messenger-smime/msgHdrViewSMIMEOverlay.xul (content/msgHdrViewSMIMEOverlay.xul)
content/messenger-smime/msgHdrViewSMIMEOverlay.js (content/msgHdrViewSMIMEOverlay.js)
content/messenger-smime/msgCompSecurityInfo.xul (content/msgCompSecurityInfo.xul)
content/messenger-smime/msgCompSecurityInfo.js (content/msgCompSecurityInfo.js)
content/messenger-smime/msgReadSecurityInfo.xul (content/msgReadSecurityInfo.xul)
content/messenger-smime/msgReadSecurityInfo.js (content/msgReadSecurityInfo.js)
content/messenger-smime/certFetchingStatus.xul (content/certFetchingStatus.xul)
content/messenger-smime/certFetchingStatus.js (content/certFetchingStatus.js)
#endif

View file

@ -0,0 +1,15 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DIRS += [
'public',
'src',
]
JAR_MANIFESTS += ['jar.mn']
JS_PREFERENCE_FILES += [
'content/smime.js',
]

View file

@ -0,0 +1,15 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
XPIDL_SOURCES += [
'nsICertPickDialogs.idl',
'nsIEncryptedSMIMEURIsSrvc.idl',
'nsIMsgSMIMECompFields.idl',
'nsIMsgSMIMEHeaderSink.idl',
'nsISMimeJSHelper.idl',
'nsIUserCertPicker.idl',
]
XPIDL_MODULE = 'msgsmime'

View file

@ -0,0 +1,30 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface nsIInterfaceRequestor;
/**
* nsICertPickDialogs
* Provides generic UI for choosing a certificate
*/
[scriptable, uuid(51d59b08-1dd2-11b2-ad4a-a51b92f8a184)]
interface nsICertPickDialogs : nsISupports
{
/**
* PickCertificate
* General purpose certificate prompter
*/
void PickCertificate(in nsIInterfaceRequestor ctx,
[array, size_is(count)] in wstring certNickList,
[array, size_is(count)] in wstring certDetailsList,
in unsigned long count,
inout long selectedIndex,
out boolean canceled);
};
%{C++
#define NS_CERTPICKDIALOGS_CONTRACTID "@mozilla.org/nsCertPickDialogs;1"
%}

View file

@ -0,0 +1,24 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* This is a private interface used exclusively by SMIME.
It provides functionality to the JS UI code,
that is only accessible from C++.
*/
#include "nsISupports.idl"
[scriptable, uuid(f86e55c9-530b-483f-91a7-10fb5b852488)]
interface nsIEncryptedSMIMEURIsService : nsISupports
{
/// Remember that this URI is encrypted.
void rememberEncrypted(in AUTF8String uri);
/// Forget that this URI is encrypted.
void forgetEncrypted(in AUTF8String uri);
/// Check if this URI is encrypted.
boolean isEncrypted(in AUTF8String uri);
};

View file

@ -0,0 +1,18 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* This is a private interface used exclusively by SMIME. NO ONE outside of extensions/smime
should have any knowledge nor should be referring to this interface.
*/
#include "nsISupports.idl"
[scriptable, uuid(338E91F9-5970-4f81-B771-0822A32B1161)]
interface nsIMsgSMIMECompFields : nsISupports
{
attribute boolean signMessage;
attribute boolean requireEncryptMessage;
};

View file

@ -0,0 +1,23 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* This is a private interface used exclusively by SMIME. NO ONE outside of extensions/smime
or the hard coded smime decryption files in mime/src should have any knowledge nor should
be referring to this interface.
*/
#include "nsISupports.idl"
interface nsIX509Cert;
[scriptable, uuid(25380FA1-E70C-4e82-B0BC-F31C2F41C470)]
interface nsIMsgSMIMEHeaderSink : nsISupports
{
void signedStatus(in long aNestingLevel, in long aSignatureStatus, in nsIX509Cert aSignerCert);
void encryptionStatus(in long aNestingLevel, in long aEncryptionStatus, in nsIX509Cert aReceipientCert);
long maxWantedNesting(); // 1 == only info on outermost nesting level wanted
};

View file

@ -0,0 +1,73 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* This is a private interface used exclusively by SMIME.
It provides functionality to the JS UI code,
that is only accessible from C++.
*/
#include "nsISupports.idl"
interface nsIMsgCompFields;
interface nsIX509Cert;
[scriptable, uuid(a54e3c8f-a000-4901-898f-fafb297b1546)]
interface nsISMimeJSHelper : nsISupports
{
/**
* Obtains detailed information about the certificate availability
* status of email recipients.
*
* @param compFields - Attributes of the composed message
*
* @param count - The number of entries in returned arrays
*
* @param emailAddresses - The list of all recipient email addresses
*
* @param certVerification - The verification/validity status of recipient certs
*
* @param certIssuedInfos - If a recipient cert was found, when has it been issued?
*
* @param certExpiredInfos - If a recipient cert was found, when will it expire?
*
* @param certs - The recipient certificates, which can contain null for not found
*
* @param canEncrypt - whether valid certificates have been found for all recipients
*
* @exception NS_ERROR_FAILURE - unexptected failure
*
* @exception NS_ERROR_OUT_OF_MEMORY - could not create the out list
*
* @exception NS_ERROR_INVALID_ARG
*/
void getRecipientCertsInfo(in nsIMsgCompFields compFields,
out unsigned long count,
[array, size_is(count)] out wstring emailAddresses,
[array, size_is(count)] out long certVerification,
[array, size_is(count)] out wstring certIssuedInfos,
[array, size_is(count)] out wstring certExpiresInfos,
[array, size_is(count)] out nsIX509Cert certs,
out boolean canEncrypt);
/**
* Obtains a list of email addresses where valid email recipient certificates
* are not yet available.
*
* @param compFields - Attributes of the composed message
*
* @param count - The number of returned email addresses
*
* @param emailAddresses - The list of email addresses without valid certs
*
* @exception NS_ERROR_FAILURE - unexptected failure
*
* @exception NS_ERROR_OUT_OF_MEMORY - could not create the out list
*
* @exception NS_ERROR_INVALID_ARG
*/
void getNoCertAddresses(in nsIMsgCompFields compFields,
out unsigned long count,
[array, size_is(count)] out wstring emailAddresses);
};

View file

@ -0,0 +1,28 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface nsIX509Cert;
interface nsIInterfaceRequestor;
[scriptable, uuid(92396323-23f2-49e0-bf98-a25a725231ab)]
interface nsIUserCertPicker : nsISupports {
nsIX509Cert pickByUsage(in nsIInterfaceRequestor ctx,
in wstring selectedNickname,
in long certUsage, // as defined by NSS enum SECCertUsage
in boolean allowInvalid,
in boolean allowDuplicateNicknames,
in AString emailAddress, // optional - if non-empty,
// skip certificates which
// have at least one e-mail
// address but do not
// include this specific one
out boolean canceled);
};
%{C++
#define NS_CERT_PICKER_CONTRACTID "@mozilla.org/user_cert_picker;1"
%}

View file

@ -0,0 +1,23 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
SOURCES += [
'nsCertPicker.cpp',
'nsEncryptedSMIMEURIsService.cpp',
'nsMsgComposeSecure.cpp',
'nsSMimeJSHelper.cpp',
]
EXTRA_COMPONENTS += [
'smime-service.js',
'smime-service.manifest',
]
FINAL_LIBRARY = 'mail'
LOCAL_INCLUDES += [
'/mozilla/security/manager/pki',
'/mozilla/security/pkix/include'
]

View file

@ -0,0 +1,471 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsCertPicker.h"
#include "MainThreadUtils.h"
#include "ScopedNSSTypes.h"
#include "cert.h"
#include "mozilla/RefPtr.h"
#include "nsCOMPtr.h"
#include "nsICertPickDialogs.h"
#include "nsIDOMWindow.h"
#include "nsIDialogParamBlock.h"
#include "nsIInterfaceRequestor.h"
#include "nsIServiceManager.h"
#include "nsIX509CertValidity.h"
#include "nsMemory.h"
#include "nsMsgComposeSecure.h"
#include "nsNSSCertificate.h"
#include "nsNSSComponent.h"
#include "nsNSSDialogHelper.h"
#include "nsNSSHelper.h"
#include "nsNSSShutDown.h"
#include "nsReadableUtils.h"
#include "nsString.h"
#include "pkix/pkixtypes.h"
using namespace mozilla;
MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertNicknames,
CERTCertNicknames,
CERT_FreeNicknames)
CERTCertNicknames*
getNSSCertNicknamesFromCertList(const UniqueCERTCertList& certList)
{
static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
nsresult rv;
nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
if (NS_FAILED(rv))
return nullptr;
nsAutoString expiredString, notYetValidString;
nsAutoString expiredStringLeadingSpace, notYetValidStringLeadingSpace;
nssComponent->GetPIPNSSBundleString("NicknameExpired", expiredString);
nssComponent->GetPIPNSSBundleString("NicknameNotYetValid", notYetValidString);
expiredStringLeadingSpace.Append(' ');
expiredStringLeadingSpace.Append(expiredString);
notYetValidStringLeadingSpace.Append(' ');
notYetValidStringLeadingSpace.Append(notYetValidString);
NS_ConvertUTF16toUTF8 aUtf8ExpiredString(expiredStringLeadingSpace);
NS_ConvertUTF16toUTF8 aUtf8NotYetValidString(notYetValidStringLeadingSpace);
return CERT_NicknameStringsFromCertList(certList.get(),
const_cast<char*>(aUtf8ExpiredString.get()),
const_cast<char*>(aUtf8NotYetValidString.get()));
}
nsresult
FormatUIStrings(nsIX509Cert* cert, const nsAutoString& nickname,
nsAutoString& nickWithSerial, nsAutoString& details)
{
if (!NS_IsMainThread()) {
NS_ERROR("nsNSSCertificate::FormatUIStrings called off the main thread");
return NS_ERROR_NOT_SAME_THREAD;
}
RefPtr<nsMsgComposeSecure> mcs = new nsMsgComposeSecure;
if (!mcs) {
return NS_ERROR_FAILURE;
}
nsAutoString info;
nsAutoString temp1;
nickWithSerial.Append(nickname);
if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertInfoIssuedFor", info))) {
details.Append(info);
details.Append(char16_t(' '));
if (NS_SUCCEEDED(cert->GetSubjectName(temp1)) && !temp1.IsEmpty()) {
details.Append(temp1);
}
details.Append(char16_t('\n'));
}
if (NS_SUCCEEDED(cert->GetSerialNumber(temp1)) && !temp1.IsEmpty()) {
details.AppendLiteral(" ");
if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertDumpSerialNo", info))) {
details.Append(info);
details.AppendLiteral(": ");
}
details.Append(temp1);
nickWithSerial.AppendLiteral(" [");
nickWithSerial.Append(temp1);
nickWithSerial.Append(char16_t(']'));
details.Append(char16_t('\n'));
}
nsCOMPtr<nsIX509CertValidity> validity;
nsresult rv = cert->GetValidity(getter_AddRefs(validity));
if (NS_SUCCEEDED(rv) && validity) {
details.AppendLiteral(" ");
if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertInfoValid", info))) {
details.Append(info);
}
if (NS_SUCCEEDED(validity->GetNotBeforeLocalTime(temp1)) && !temp1.IsEmpty()) {
details.Append(char16_t(' '));
if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertInfoFrom", info))) {
details.Append(info);
details.Append(char16_t(' '));
}
details.Append(temp1);
}
if (NS_SUCCEEDED(validity->GetNotAfterLocalTime(temp1)) && !temp1.IsEmpty()) {
details.Append(char16_t(' '));
if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertInfoTo", info))) {
details.Append(info);
details.Append(char16_t(' '));
}
details.Append(temp1);
}
details.Append(char16_t('\n'));
}
if (NS_SUCCEEDED(cert->GetKeyUsages(temp1)) && !temp1.IsEmpty()) {
details.AppendLiteral(" ");
if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertDumpKeyUsage", info))) {
details.Append(info);
details.AppendLiteral(": ");
}
details.Append(temp1);
details.Append(char16_t('\n'));
}
UniqueCERTCertificate nssCert(cert->GetCert());
if (!nssCert) {
return NS_ERROR_FAILURE;
}
nsAutoString firstEmail;
const char* aWalkAddr;
for (aWalkAddr = CERT_GetFirstEmailAddress(nssCert.get())
;
aWalkAddr
;
aWalkAddr = CERT_GetNextEmailAddress(nssCert.get(), aWalkAddr))
{
NS_ConvertUTF8toUTF16 email(aWalkAddr);
if (email.IsEmpty())
continue;
if (firstEmail.IsEmpty()) {
// If the first email address from the subject DN is also present
// in the subjectAltName extension, GetEmailAddresses() will return
// it twice (as received from NSS). Remember the first address so that
// we can filter out duplicates later on.
firstEmail = email;
details.AppendLiteral(" ");
if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertInfoEmail", info))) {
details.Append(info);
details.AppendLiteral(": ");
}
details.Append(email);
}
else {
// Append current address if it's different from the first one.
if (!firstEmail.Equals(email)) {
details.AppendLiteral(", ");
details.Append(email);
}
}
}
if (!firstEmail.IsEmpty()) {
// We got at least one email address, so we want a newline
details.Append(char16_t('\n'));
}
if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertInfoIssuedBy", info))) {
details.Append(info);
details.Append(char16_t(' '));
if (NS_SUCCEEDED(cert->GetIssuerName(temp1)) && !temp1.IsEmpty()) {
details.Append(temp1);
}
details.Append(char16_t('\n'));
}
if (NS_SUCCEEDED(mcs->GetSMIMEBundleString(u"CertInfoStoredIn", info))) {
details.Append(info);
details.Append(char16_t(' '));
if (NS_SUCCEEDED(cert->GetTokenName(temp1)) && !temp1.IsEmpty()) {
details.Append(temp1);
}
}
// the above produces the following output:
//
// Issued to: $subjectName
// Serial number: $serialNumber
// Valid from: $starting_date to $expiration_date
// Certificate Key usage: $usages
// Email: $address(es)
// Issued by: $issuerName
// Stored in: $token
return rv;
}
NS_IMPL_ISUPPORTS(nsCertPicker, nsICertPickDialogs, nsIUserCertPicker)
nsCertPicker::nsCertPicker()
{
}
nsCertPicker::~nsCertPicker()
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return;
}
shutdown(ShutdownCalledFrom::Object);
}
nsresult
nsCertPicker::Init()
{
nsresult rv;
nsCOMPtr<nsISupports> psm = do_GetService("@mozilla.org/psm;1", &rv);
return rv;
}
NS_IMETHODIMP
nsCertPicker::PickCertificate(nsIInterfaceRequestor *ctx,
const char16_t **certNickList,
const char16_t **certDetailsList,
uint32_t count,
int32_t *selectedIndex,
bool *canceled)
{
nsresult rv;
uint32_t i;
*canceled = false;
nsCOMPtr<nsIDialogParamBlock> block =
do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID);
if (!block) return NS_ERROR_FAILURE;
block->SetNumberStrings(1+count*2);
for (i = 0; i < count; i++) {
rv = block->SetString(i, certNickList[i]);
if (NS_FAILED(rv)) return rv;
}
for (i = 0; i < count; i++) {
rv = block->SetString(i+count, certDetailsList[i]);
if (NS_FAILED(rv)) return rv;
}
rv = block->SetInt(0, count);
if (NS_FAILED(rv)) return rv;
rv = block->SetInt(1, *selectedIndex);
if (NS_FAILED(rv)) return rv;
rv = nsNSSDialogHelper::openDialog(nullptr,
"chrome://messenger/content/certpicker.xul",
block);
if (NS_FAILED(rv)) return rv;
int32_t status;
rv = block->GetInt(0, &status);
if (NS_FAILED(rv)) return rv;
*canceled = (status == 0)?true:false;
if (!*canceled) {
rv = block->GetInt(1, selectedIndex);
}
return rv;
}
NS_IMETHODIMP nsCertPicker::PickByUsage(nsIInterfaceRequestor *ctx,
const char16_t *selectedNickname,
int32_t certUsage,
bool allowInvalid,
bool allowDuplicateNicknames,
const nsAString &emailAddress,
bool *canceled,
nsIX509Cert **_retval)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown()) {
return NS_ERROR_NOT_AVAILABLE;
}
int32_t selectedIndex = -1;
bool selectionFound = false;
char16_t **certNicknameList = nullptr;
char16_t **certDetailsList = nullptr;
CERTCertListNode* node = nullptr;
nsresult rv = NS_OK;
{
// Iterate over all certs. This assures that user is logged in to all hardware tokens.
nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
UniqueCERTCertList allcerts(PK11_ListCerts(PK11CertListUnique, ctx));
}
/* find all user certs that are valid for the specified usage */
/* note that we are allowing expired certs in this list */
UniqueCERTCertList certList(
CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(),
(SECCertUsage)certUsage,
!allowDuplicateNicknames,
!allowInvalid,
ctx));
if (!certList) {
return NS_ERROR_NOT_AVAILABLE;
}
/* if a (non-empty) emailAddress argument is supplied to PickByUsage, */
/* remove non-matching certificates from the candidate list */
if (!emailAddress.IsEmpty()) {
node = CERT_LIST_HEAD(certList);
while (!CERT_LIST_END(node, certList)) {
/* if the cert has at least one e-mail address, check if suitable */
if (CERT_GetFirstEmailAddress(node->cert)) {
RefPtr<nsNSSCertificate> tempCert(nsNSSCertificate::Create(node->cert));
bool match = false;
rv = tempCert->ContainsEmailAddress(emailAddress, &match);
if (NS_FAILED(rv)) {
return rv;
}
if (!match) {
/* doesn't contain the specified address, so remove from the list */
CERTCertListNode* freenode = node;
node = CERT_LIST_NEXT(node);
CERT_RemoveCertListNode(freenode);
continue;
}
}
node = CERT_LIST_NEXT(node);
}
}
UniqueCERTCertNicknames nicknames(getNSSCertNicknamesFromCertList(certList));
if (!nicknames) {
return NS_ERROR_NOT_AVAILABLE;
}
certNicknameList = (char16_t **)moz_xmalloc(sizeof(char16_t *) * nicknames->numnicknames);
certDetailsList = (char16_t **)moz_xmalloc(sizeof(char16_t *) * nicknames->numnicknames);
if (!certNicknameList || !certDetailsList) {
free(certNicknameList);
free(certDetailsList);
return NS_ERROR_OUT_OF_MEMORY;
}
int32_t CertsToUse;
for (CertsToUse = 0, node = CERT_LIST_HEAD(certList.get());
!CERT_LIST_END(node, certList.get()) &&
CertsToUse < nicknames->numnicknames;
node = CERT_LIST_NEXT(node)
)
{
RefPtr<nsNSSCertificate> tempCert(nsNSSCertificate::Create(node->cert));
if (tempCert) {
nsAutoString i_nickname(NS_ConvertUTF8toUTF16(nicknames->nicknames[CertsToUse]));
nsAutoString nickWithSerial;
nsAutoString details;
if (!selectionFound) {
/* for the case when selectedNickname refers to a bare nickname */
if (i_nickname == nsDependentString(selectedNickname)) {
selectedIndex = CertsToUse;
selectionFound = true;
}
}
if (NS_SUCCEEDED(FormatUIStrings(tempCert, i_nickname, nickWithSerial,
details))) {
certNicknameList[CertsToUse] = ToNewUnicode(nickWithSerial);
certDetailsList[CertsToUse] = ToNewUnicode(details);
if (!selectionFound) {
/* for the case when selectedNickname refers to nickname + serial */
if (nickWithSerial == nsDependentString(selectedNickname)) {
selectedIndex = CertsToUse;
selectionFound = true;
}
}
}
else {
certNicknameList[CertsToUse] = nullptr;
certDetailsList[CertsToUse] = nullptr;
}
++CertsToUse;
}
}
if (CertsToUse) {
nsCOMPtr<nsICertPickDialogs> dialogs;
rv = getNSSDialogs(getter_AddRefs(dialogs), NS_GET_IID(nsICertPickDialogs),
NS_CERTPICKDIALOGS_CONTRACTID);
if (NS_SUCCEEDED(rv)) {
// Show the cert picker dialog and get the index of the selected cert.
rv = dialogs->PickCertificate(ctx, (const char16_t**)certNicknameList,
(const char16_t**)certDetailsList,
CertsToUse, &selectedIndex, canceled);
}
}
int32_t i;
for (i = 0; i < CertsToUse; ++i) {
free(certNicknameList[i]);
free(certDetailsList[i]);
}
free(certNicknameList);
free(certDetailsList);
if (!CertsToUse) {
return NS_ERROR_NOT_AVAILABLE;
}
if (NS_SUCCEEDED(rv) && !*canceled) {
for (i = 0, node = CERT_LIST_HEAD(certList);
!CERT_LIST_END(node, certList);
++i, node = CERT_LIST_NEXT(node)) {
if (i == selectedIndex) {
RefPtr<nsNSSCertificate> cert = nsNSSCertificate::Create(node->cert);
if (!cert) {
rv = NS_ERROR_OUT_OF_MEMORY;
break;
}
cert.forget(_retval);
break;
}
}
}
return rv;
}

View file

@ -0,0 +1,36 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsCertPicker_h
#define nsCertPicker_h
#include "nsICertPickDialogs.h"
#include "nsIUserCertPicker.h"
#include "nsNSSShutDown.h"
#define NS_CERT_PICKER_CID \
{ 0x735959a1, 0xaf01, 0x447e, { 0xb0, 0x2d, 0x56, 0xe9, 0x68, 0xfa, 0x52, 0xb4 } }
class nsCertPicker : public nsICertPickDialogs
, public nsIUserCertPicker
, public nsNSSShutDownObject
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSICERTPICKDIALOGS
NS_DECL_NSIUSERCERTPICKER
nsCertPicker();
// Nothing to actually release.
virtual void virtualDestroyNSSReference() override {}
nsresult Init();
protected:
virtual ~nsCertPicker();
};
#endif // nsCertPicker_h

Some files were not shown because too many files have changed in this diff Show more