Bug 954193 - reorganize purplexpcom, part 2: rewrite the core service, account list, status handling and protocol plugin loading in JS components that don't depend on libpurple.

--HG--
rename : purple/purplexpcom/public/purpleIProtocol.idl => chat/components/public/prplIProtocol.idl
This commit is contained in:
Florian Quèze 2011-11-09 01:16:17 +01:00
Родитель b42adbfac2
Коммит a53db18a77
47 изменённых файлов: 2019 добавлений и 260 удалений

Просмотреть файл

@ -45,9 +45,14 @@ MODULE = chat
XPIDLSRCS = \
ibILogger.idl \
imIAccount.idl \
imIAccountsService.idl \
imICommandsService.idl \
imIContactsService.idl \
imIConversationsService.idl \
imICoreService.idl \
imIUserStatusInfo.idl \
prplIProtocol.idl \
purpleIConversation.idl \
$(NULL)

Просмотреть файл

@ -38,7 +38,7 @@
#include "nsISupports.idl"
#include "nsISimpleEnumerator.idl"
interface purpleIAccount;
interface imIAccount;
interface imIAccountBuddy;
interface imIBuddy;
interface imIContact;
@ -56,5 +56,5 @@ interface ibILogger: nsISupports {
nsISimpleEnumerator getLogsForBuddy(in imIBuddy aBuddy);
nsISimpleEnumerator getLogsForContact(in imIContact aContact);
nsISimpleEnumerator getLogsForConversation(in purpleIConversation aConversation);
nsISimpleEnumerator getSystemLogsForAccount(in purpleIAccount aAccount);
nsISimpleEnumerator getSystemLogsForAccount(in imIAccount aAccount);
};

Просмотреть файл

@ -0,0 +1,296 @@
/* ***** 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 the Instantbird messenging client, released
* 2007.
*
* The Initial Developer of the Original Code is
* Florian QUEZE <florian@instantbird.org>.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* 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 "nsISupports.idl"
#include "purpleIConversation.idl"
#include "purpleIProxy.idl"
#include "imIUserStatusInfo.idl"
interface imITag;
interface imIBuddy;
interface imIAccountBuddy;
interface imIAccount;
interface prplIProtocol;
/*
* Used to join chat rooms.
*/
[ptr] native GHashTablePtr(GHashTable);
[scriptable, uuid(ea7ab156-d25e-46cc-93ef-29f77b3c0795)]
interface purpleIChatRoomFieldValues: nsISupports {
AUTF8String getValue(in AUTF8String aIdentifier);
void setValue(in AUTF8String aIdentifier, in AUTF8String aValue);
[noscript] readonly attribute GHashTablePtr hashTable;
};
[scriptable, uuid(19dff981-b125-4a70-bc1a-efc783d07137)]
interface purpleIChatRoomField: nsISupports {
readonly attribute AUTF8String label;
readonly attribute AUTF8String identifier;
readonly attribute boolean required;
const short TYPE_TEXT = 0;
const short TYPE_PASSWORD = 1;
const short TYPE_INT = 2;
readonly attribute short type;
readonly attribute long min;
readonly attribute long max;
};
/*
* This interface should be implemented by the protocol plugin.
*/
[scriptable, uuid(fb1b29cb-63ba-4335-9f44-63aea3f616a3)]
interface prplIAccount: nsISupports {
readonly attribute imIAccount imAccount;
// observe should only be called by the imIAccount
// implementation to report user status changes that affect this account.
void observe(in nsISupports aObj, in string aEvent,
[optional] in wstring aData);
/* Uninitialize the prplIAccount instance. This is typically done
automatically at shutdown (by the core service) or as part of
the 'remove' method. */
void unInit();
void connect();
void disconnect();
purpleIConversation createConversation(in AUTF8String aName);
// Used when the user wants to add a buddy to the buddy list
void addBuddy(in imITag aTag, in AUTF8String aName);
// Used while loading the buddy list at startup.
imIAccountBuddy loadBuddy(in imIBuddy aBuddy, in imITag aTag);
/* Request more info on a buddy (typically a chat buddy).
* The result (if any) will be provided by a user-info-received
* notification dispatched through the observer service:
* - aSubject will be an nsISimpleEnumerator of purpleITooltipInfo,
* Do NOT store aSubject. Use it right away. The memory it points to
* will be freed right after the notification is dispatched.
* - aData will be aBuddyName.
*/
void requestBuddyInfo(in AUTF8String aBuddyName);
readonly attribute boolean canJoinChat;
nsISimpleEnumerator getChatRoomFields();
purpleIChatRoomFieldValues getChatRoomDefaultFieldValues([optional] in AUTF8String aDefaultChatName);
/*
* Create a new chat conversation if it doesn't already exist.
*/
void joinChat(in purpleIChatRoomFieldValues aComponents);
readonly attribute AUTF8String normalizedName;
attribute purpleIProxyInfo proxyInfo;
// protocol specific options: those functions set the protocol
// specific options for the PurpleAccount
void setBool(in string aName, in boolean aVal);
void setInt(in string aName, in long aVal);
void setString(in string aName, in string aVal);
/* When a connection error occurred, this value indicates the type of error */
readonly attribute short connectionErrorReason;
/* Possible connection error reasons:
ERROR_NETWORK_ERROR and ERROR_ENCRYPTION_ERROR are not fatal and
should enable the automatic reconnection feature. */
const short NO_ERROR = -1;
const short ERROR_NETWORK_ERROR = 0;
const short ERROR_INVALID_USERNAME = 1;
const short ERROR_AUTHENTICATION_FAILED = 2;
const short ERROR_AUTHENTICATION_IMPOSSIBLE = 3;
const short ERROR_NO_SSL_SUPPORT = 4;
const short ERROR_ENCRYPTION_ERROR = 5;
const short ERROR_NAME_IN_USE = 6;
const short ERROR_INVALID_SETTINGS = 7;
const short ERROR_CERT_NOT_PROVIDED = 8;
const short ERROR_CERT_UNTRUSTED = 9;
const short ERROR_CERT_EXPIRED = 10;
const short ERROR_CERT_NOT_ACTIVATED = 11;
const short ERROR_CERT_HOSTNAME_MISMATCH = 12;
const short ERROR_CERT_FINGERPRINT_MISMATCH = 13;
const short ERROR_CERT_SELF_SIGNED = 14;
const short ERROR_CERT_OTHER_ERROR = 15;
const short ERROR_OTHER_ERROR = 16;
/* From PurpleConnectionFlags */
// PURPLE_CONNECTION_HTML
// Connection sends/receives in 'HTML'.
readonly attribute boolean HTMLEnabled;
// PURPLE_CONNECTION_NO_BGCOLOR
// Connection does not send/receive background colors.
readonly attribute boolean noBackgroundColors;
// PURPLE_CONNECTION_AUTO_RESP
// Send auto responses when away.
readonly attribute boolean autoResponses;
// PURPLE_CONNECTION_FORMATTING_WBFO
// The text buffer must be formatted as a whole.
readonly attribute boolean singleFormatting;
// PURPLE_CONNECTION_NO_NEWLINES
// No new lines are allowed in outgoing messages.
readonly attribute boolean noNewlines;
// PURPLE_CONNECTION_NO_FONTSIZE
// Connection does not send/receive font sizes.
readonly attribute boolean noFontSizes;
// PURPLE_CONNECTION_NO_URLDESC
// Connection does not support descriptions with links.
readonly attribute boolean noUrlDesc;
// PURPLE_CONNECTION_NO_IMAGES
// Connection does not support sending of images.
readonly attribute boolean noImages;
// This is currently used only by Twitter.
readonly attribute PRInt32 maxMessageLength;
};
/* This interface should be implemented by the im core. It inherits
from prplIAccount and in most cases will forward the calls for the
inherited members to a prplIAccount account instance implemented by
the protocol plugin. */
[scriptable, uuid(20a85b44-e220-4f23-85bf-f8523d1a2b08)]
interface imIAccount: prplIAccount {
/* Check if autologin is enabled for this account, connect it now. */
void checkAutoLogin();
/* Delete the account (from the preferences, mozStorage, and call unInit). */
void remove();
/* Cancel the timer that automatically reconnects the account that were
disconnected with a non fatal error */
void cancelReconnection();
readonly attribute AUTF8String name;
readonly attribute AUTF8String id;
readonly attribute PRUint32 numericId;
readonly attribute prplIProtocol protocol;
readonly attribute prplIAccount prplAccount;
// Save account specific preferences to disk.
void save();
attribute boolean autoLogin;
/* This is the value when the preference firstConnectionState is not set.
It indicates that the account has already been successfully connected at
least once with the current parameters. */
const short FIRST_CONNECTION_OK = 0;
/* Set when the account has never had a successful connection
with the current parameters */
const short FIRST_CONNECTION_UNKNOWN = 1;
/* Set when the account is trying to connect for the first time
with the current parameters (removed after a successsful connection) */
const short FIRST_CONNECTION_PENDING = 2;
/* Set at startup when the previous state was pending */
const short FIRST_CONNECTION_CRASHED = 4;
attribute short firstConnectionState;
// FIXME password should be in password manager
attribute AUTF8String password;
attribute AUTF8String alias;
/* While an account is connecting, this attribute contains a message
indicating the current step of the connection */
readonly attribute AUTF8String connectionStateMsg;
/* Number of the reconnection attempt
* 0 means that no automatic reconnection currently pending
* n means the nth reconnection attempt is pending
*/
readonly attribute PRUint16 reconnectAttempt;
/* Time stamp of the next reconnection attempt */
readonly attribute PRInt64 timeOfNextReconnect;
/* Time stamp of the last connection (value not reliable if not connected) */
readonly attribute PRInt64 timeOfLastConnect;
/* Additional possible connection error reasons:
* (Use a big enough number that it can't conflict with error
* codes used in prplIAccount).
*/
const short ERROR_UNKNOWN_PRPL = 42;
const short ERROR_CRASHED = 43;
const short ERROR_MISSING_PASSWORD = 44;
/* A message describing the connection error */
readonly attribute AUTF8String connectionErrorMessage;
/* Info about the connection state and flags */
const short STATE_DISCONNECTED = 0;
const short STATE_CONNECTED = 1;
const short STATE_CONNECTING = 2;
const short STATE_DISCONNECTING = 3;
readonly attribute short connectionState;
/* The following 4 properties use the above connectionState value. */
readonly attribute boolean disconnected;
readonly attribute boolean connected;
readonly attribute boolean connecting;
readonly attribute boolean disconnecting;
/* The imIUserStatusInfo instance this account should observe for
status changes. When this is null (the default value), the
account will observe the global status. */
attribute imIUserStatusInfo observedStatusInfo;
// Same as above, but never null (it fallbacks to the global status info).
attribute imIUserStatusInfo statusInfo;
// imIAccount also implements an observe method but this
// observe should only be called by the prplIAccount
// implementations to report connection status changes.
};

Просмотреть файл

@ -0,0 +1,97 @@
/* ***** 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 the Instantbird messenging client, released
* 2007.
*
* The Initial Developer of the Original Code is
* Florian QUEZE <florian@instantbird.org>.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* 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 "nsISupports.idl"
#include "nsISimpleEnumerator.idl"
#include "imIAccount.idl"
[scriptable, uuid(b3b6459a-5c26-47b8-8e9c-ba838b6f632a)]
interface imIAccountsService: nsISupports {
void initAccounts();
void unInitAccounts();
/* This attribute is set to AUTOLOGIN_ENABLED by default. It can be set to
any other value before the initialization of this service to prevent
accounts with autoLogin enabled from being connected when libpurple is
initialized.
Any value other than the ones listed below will disable autoLogin and
display a generic message in the Account Manager. */
attribute short autoLoginStatus;
const short AUTOLOGIN_ENABLED = 0;
const short AUTOLOGIN_USER_DISABLED = 1;
const short AUTOLOGIN_SAFE_MODE = 2;
const short AUTOLOGIN_CRASH = 3;
const short AUTOLOGIN_START_OFFLINE = 4;
/* The method should be used to connect all accounts with autoLogin enabled.
Some use cases:
- if the autologin was disabled at startup
- after a loss of internet connectivity that disconnected all accounts.
*/
void processAutoLogin();
imIAccount getAccountById(in AUTF8String aAccountId);
/* will throw NS_ERROR_FAILURE if not found */
imIAccount getAccountByNumericId(in PRUint32 aAccountId);
nsISimpleEnumerator getAccounts();
/* will fire the event account-added */
imIAccount createAccount(in AUTF8String aName, in AUTF8String aPrpl);
/* will fire the event account-removed */
void deleteAccount(in AUTF8String aAccountId);
};
/*
account related notifications sent to nsIObserverService:
- account-added: a new account has been created
- account-removed: the account has been deleted
- account-connecting: the account is being connected
- account-connected: the account is now connected
- account-connect-error: the account is disconnect with an error.
(before account-disconnecting)
- account-disconnecting: the account is being disconnected
- account-disconnected: the account is now disconnected
- account-updated: when some settings have changed
- account-list-updated: when the list of account is reordered.
These events can be watched using an nsIObserver.
The associated imIAccount will be given as a parameter
(except for account-list-updated).
*/

Просмотреть файл

@ -44,7 +44,8 @@ interface imITag;
interface imIContact;
interface imIBuddy;
interface imIAccountBuddy;
interface purpleIProtocol;
interface imIAccount;
interface prplIProtocol;
[scriptable, uuid(f1619b49-310b-47aa-ab1c-238aba084c62)]
interface imIContactsService: nsISupports {
@ -54,7 +55,7 @@ interface imIContactsService: nsISupports {
imIContact getContactById(in long aId);
imIBuddy getBuddyById(in long aId);
imIBuddy getBuddyByNameAndProtocol(in AUTF8String aNormalizedName,
in purpleIProtocol aPrpl);
in prplIProtocol aPrpl);
// These 3 functions are called by the protocol plugins when
// synchronizing the buddy list with the server stored list,
@ -63,6 +64,22 @@ interface imIContactsService: nsISupports {
void accountBuddyRemoved(in imIAccountBuddy aAccountBuddy);
void accountBuddyMoved(in imIAccountBuddy aAccountBuddy,
in imITag aOldTag, in imITag aNewTag);
// These methods are called by the imIAccountsService implementation
// to keep the accounts table in sync with accounts stored in the
// preferences.
// Called when an account is created or loaded to store the new
// account or ensure it doesn't conflict with an existing account
// (to detect database corruption).
// Will throw if a stored account has the id aId but a different
// username or prplId.
void storeAccount(in PRUint32 aId, in AUTF8String aUserName,
in AUTF8String aPrplId);
// Check if an account id already exists in the database.
boolean accountIdExists(in PRUint32 aId);
// Called when deleting an account to remove it from blist.sqlite.
void forgetAccount(in PRUint32 aId);
};
[scriptable, uuid(f799a9c2-23f2-4fd1-96fb-515bad238f8c)]
@ -255,7 +272,7 @@ interface imIContact: imIStatusInfo {
[scriptable, uuid(fea582a0-3839-4d80-bcab-0ff82ae8f97f)]
interface imIBuddy: imIStatusInfo {
readonly attribute long id;
readonly attribute purpleIProtocol protocol;
readonly attribute prplIProtocol protocol;
readonly attribute AUTF8String userName; // may be formatted
readonly attribute AUTF8String normalizedName; // normalized userName
// The optional server alias is in displayName (inherited from imIStatusInfo)
@ -330,7 +347,7 @@ interface imIAccountBuddy: imIStatusInfo {
// Contacts service when accountBuddyAdded is called on this
// instance of imIAccountBuddy.
attribute imIBuddy buddy;
readonly attribute purpleIAccount account;
readonly attribute imIAccount account;
// Setting the tag will move the buddy to a different group on the
// server-stored buddy list.
attribute imITag tag;

Просмотреть файл

@ -93,6 +93,6 @@ interface imIConversationsService: nsISupports {
nsISimpleEnumerator getConversations();
purpleIConversation getConversationById(in unsigned long aId);
purpleIConversation getConversationByNameAndAccount(in AUTF8String aName,
in purpleIAccount aAccount,
in imIAccount aAccount,
in boolean aIsChat);
};

Просмотреть файл

@ -0,0 +1,54 @@
/* ***** 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 the Instantbird messenging client, released
* 2007.
*
* The Initial Developer of the Original Code is
* Florian QUEZE <florian@instantbird.org>.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* 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 "nsISupports.idl"
#include "imIUserStatusInfo.idl"
#include "nsISimpleEnumerator.idl"
#include "prplIProtocol.idl"
[scriptable, uuid(205d4b2b-1ccf-4879-9ef1-f08942566151)]
interface imICoreService: nsISupports {
void init();
void quit(); // will emit a prpl-quit notification.
// returns an enumerator on a pplIProtocol array
nsISimpleEnumerator getProtocols();
prplIProtocol getProtocolById(in AUTF8String aProtocolId);
readonly attribute imIUserStatusInfo globalUserStatus;
};

Просмотреть файл

@ -0,0 +1,76 @@
/* ***** 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 the Instantbird messenging client, released
* 2010.
*
* The Initial Developer of the Original Code is
* Florian QUEZE <florian@instantbird.org>.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* 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 "nsISupports.idl"
#include "nsIObserver.idl"
//forward declarations
interface nsILocalFile;
interface nsIFileURL;
[scriptable, uuid(817918fa-1f4b-4254-9cdb-f906da91c45d)]
interface imIUserStatusInfo: nsISupports {
readonly attribute AUTF8String statusText;
// See imIStatusInfo in imIContactsService.idl for the values.
readonly attribute short statusType;
// Only works with STATUS_OFFLINE, STATUS_UNAVAILABLE, STATUS_AWAY,
// STATUS_AVAILABLE and STATUS_INVISIBLE.
// - When called with the status type STATUS_UNSET, only the status
// message will be changed.
// - When called with STATUS_OFFLINE, the aMessage parameter is ignored.
void setStatus(in short aStatus, in AUTF8String aMessage);
/* Will fire an user-icon-changed notificaton. */
void setUserIcon(in nsILocalFile aIconFile);
nsIFileURL getUserIcon();
/* The setter will fire an user-display-name-changed notificaton. */
attribute AUTF8String displayName;
void addObserver(in nsIObserver aObserver);
void removeObserver(in nsIObserver aObserver);
/* Observers will receive the following notifications:
* status-changed (when either the status type or text has changed)
* user-icon-changed
* user-display-name-changed
* idle-time-changed
*/
};

Просмотреть файл

@ -37,22 +37,15 @@
#include "nsISupports.idl"
#include "nsISimpleEnumerator.idl"
#include "imIAccount.idl"
/*
* This is the libpurple protocol
*/
[scriptable, uuid(7d302db0-3813-4c51-8372-c7eb5fc9f3d3)]
interface prplIProtocol: nsISupports {
// aId is the prpl id, this method is used so that classes
// implementing several protocol plugins can know which protocol is
// desired for this instance.
void init(in AUTF8String aId);
%{C++
#pragma GCC visibility push(default)
#include <libpurple/prpl.h>
#pragma GCC visibility pop
%}
[ptr] native PurpleNativePluginProtocolInfo(PurplePluginProtocolInfo);
interface purpleIAccount;
[scriptable, uuid(0a51b511-4ce7-4f4c-a109-43023b72fc5a)]
interface purpleIProtocol: nsISupports {
readonly attribute AUTF8String name;
readonly attribute AUTF8String id;
readonly attribute AUTF8String normalizedName;
@ -123,9 +116,9 @@ interface purpleIProtocol: nsISupports {
// account, or uses the Mozilla proxy settings for all accounts.
readonly attribute boolean usePurpleProxy;
// The key is the unique identifier needed to get account data
// stored in the preferences.
purpleIAccount getAccount(in AUTF8String aKey, in AUTF8String aName);
// Get the protocol specific part of an already initialized
// imIAccount instance.
prplIAccount getAccount(in imIAccount aImAccount);
};
[scriptable, uuid(20c4971a-f7c2-4781-8e85-69fee7b83a3d)]
@ -138,7 +131,3 @@ interface purpleIUsernameSplit: nsISupports {
the end of the string, PR_FALSE otherwise. */
readonly attribute PRBool reverse;
};
%{C++
#define JS_PROTOCOL_PLUGIN_CATEGORY "js-protocol-plugin"
%}

Просмотреть файл

@ -41,7 +41,7 @@
#include "nsIObserver.idl"
interface imIAccountBuddy;
interface purpleIAccount;
interface imIAccount;
interface nsIURI;
interface nsIDOMDocument;
@ -56,7 +56,7 @@ interface purpleIConversation: nsISupports {
readonly attribute boolean isChat;
/* The account used for this conversation */
readonly attribute purpleIAccount account;
readonly attribute imIAccount account;
/* The name of the conversation, typically in English */
readonly attribute AUTF8String name;

Просмотреть файл

@ -42,14 +42,17 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
EXTRA_COMPONENTS = \
imAccounts.manifest \
imCommands.js imCommands.manifest \
imContacts.js imContacts.manifest \
imConversations.js imConversations.manifest \
imCommands.js imCommands.manifest \
imCore.js imCore.manifest \
logger.manifest \
smileProtocolHandler.js smileProtocolHandler.manifest \
$(NULL)
EXTRA_PP_COMPONENTS = \
imAccounts.js \
logger.js \
$(NULL)

Просмотреть файл

@ -0,0 +1,741 @@
/* ***** 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 the Instantbird messenging client, released
* 2011.
*
* The Initial Developer of the Original Code is
* Florian QUEZE <florian@instantbird.org>.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Romain Bezut <romain@bezut.info>
*
* 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 ***** */
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource:///modules/imXPCOMUtils.jsm");
Cu.import("resource:///modules/imServices.jsm");
const kPrefAutologinPending = "messenger.accounts.autoLoginPending";
const kPrefMessengerAccounts = "messenger.accounts";
const kPrefAccountPrefix = "messenger.account.";
const kAccountKeyPrefix = "account";
const kAccountOptionPrefPrefix = "options.";
const kPrefAccountName = "name";
const kPrefAccountPrpl = "prpl";
const kPrefAccountPassword = "password";
const kPrefAccountAutoLogin = "autoLogin";
const kPrefAccountAlias = "alias";
const kPrefAccountFirstConnectionState = "firstConnectionState";
var SavePrefTimer = {
saveNow: function() {
if (this._timer) {
clearTimeout(this._timer);
this._timer = null;
}
Services.prefs.savePrefFile(null);
},
_timer: null,
unInitTimer: function() {
if (this._timer)
this.saveNow();
},
initTimer: function() {
if (!this._timer)
this._timer = setTimeout(this.saveNow.bind(this), 5000);
}
};
var AutoLoginCounter = {
_count: 0,
startAutoLogin: function() {
++this._count;
if (this._count != 1)
return;
Services.prefs.setIntPref(kPrefAutologinPending, Date.now() / 1000);
SavePrefTimer.saveNow();
},
finishedAutoLogin: function() {
--this._count;
if (this._count != 0)
return;
Services.prefs.deleteBranch(kPrefAutologinPending);
SavePrefTimer.initTimer();
}
};
function UnknownProtocol(aPrplId)
{
this.id = aPrplId;
}
UnknownProtocol.prototype = {
__proto__: ClassInfo("prplIProtocol", "Unknown protocol"),
get name() "",
get normalizedName() this.name,
get iconBaseURI() "chrome://instantbird/skin/prpl-unknown/",
getOptions: function() EmptyEnumerator,
getUsernameSplit: function() EmptyEnumerator,
get usernameEmptyText() "",
getAccount: function(aKey, aName) { throw Cr.NS_ERROR_NOT_IMPLEMENTED; },
accountExists: function() { throw Cr.NS_ERROR_NOT_IMPLEMENTED; },
// false seems an acceptable default for all options
// (they should never be called anyway).
get uniqueChatName() false,
get chatHasTopic() false,
get noPassword() false,
get newMailNotification() false,
get imagesInIM() false,
get passwordOptional() true,
get usePointSize() true,
get registerNoScreenName() false,
get slashCommandsNative() false,
get usePurpleProxy() false
};
// aName and aPrplId are provided as parameter only if this is a new
// account that doesn't exist in the preferences. In this case, these
// 2 values should be stored.
function imAccount(aKey, aName, aPrplId)
{
if (aKey.indexOf(kAccountKeyPrefix) != 0)
throw Cr.NS_ERROR_INVALID_ARG;
this.id = aKey;
this.numericId = parseInt(aKey.substr(kAccountKeyPrefix.length));
this.prefBranch = Services.prefs.getBranch(kPrefAccountPrefix + aKey + ".");
if (aName) {
this.name = aName;
this.prefBranch.setCharPref(kPrefAccountName, aName);
this.firstConnectionState = Ci.imIAccount.FIRST_CONNECTION_UNKNOWN;
}
else
this.name = this.prefBranch.getCharPref(kPrefAccountName);
let prplId = aPrplId;
if (prplId)
this.prefBranch.setCharPref(kPrefAccountPrpl, prplId);
else
prplId = this.prefBranch.getCharPref(kPrefAccountPrpl);
// Get the protocol plugin, or fallback to an UnknownProtocol instance.
this.protocol = Services.core.getProtocolById(prplId);
if (!this.protocol) {
this.protocol = new UnknownProtocol(prplId);
this._connectionErrorReason = Ci.imIAccount.ERROR_UNKNOWN_PRPL;
return;
}
// Ensure the account is correctly stored in blist.sqlite.
Services.contacts.storeAccount(this.numericId, this.name, prplId);
// Get the prplIAccount from the protocol plugin.
this.prplAccount = this.protocol.getAccount(this);
// Send status change notifications to the account.
this.observedStatusInfo = null; // (To execute the setter).
// If we have never finished the first connection attempt for this account,
// mark the account as having caused a crash.
if (this.firstConnectionState == Ci.imIAccount.FIRST_CONNECTION_PENDING)
this.firstConnectionState = Ci.imIAccount.FIRST_CONNECTION_CRASHED;
// Check for errors that should prevent connection attempts.
if (this._passwordRequired && !this.password)
this._connectionErrorReason = Ci.imIAccount.ERROR_MISSING_PASSWORD;
else if (this.firstConnectionState == Ci.imIAccount.FIRST_CONNECTION_CRASHED)
this._connectionErrorReason = Ci.imIAccount.ERROR_CRASHED;
}
imAccount.prototype = {
__proto__: ClassInfo(["imIAccount", "prplIAccount"], "im account object"),
name: "",
id: "",
numericId: 0,
protocol: null,
prplAccount: null,
connectionState: Ci.imIAccount.STATE_DISCONNECTED,
connectionStateMsg: "",
connectionErrorMessage: "",
_connectionErrorReason: Ci.prplIAccount.NO_ERROR,
get connectionErrorReason() {
if (this._connectionErrorReason != Ci.prplIAccount.NO_ERROR)
return this._connectionErrorReason;
else
return this.prplAccount.connectionErrorReason;
},
observe: function(aSubject, aTopic, aData) {
if (aTopic == "account-connect-progress")
this.connectionStateMsg = aData;
else if (aTopic == "account-connecting") {
if (this.prplAccount.connectionErrorReason != Ci.prplIAccount.NO_ERROR) {
delete this.connectionErrorMessage;
if (this.timeOfNextReconnect - Date.now() > 1000) {
// This is a manual reconnection, reset the auto-reconnect stuff
this.timeOfLastConnect = 0;
this.cancelReconnection();
}
}
if (this.firstConnectionState != Ci.imIAccount.FIRST_CONNECTION_OK)
this.firstConnectionState = Ci.imIAccount.FIRST_CONNECTION_PENDING;
this.connectionState = Ci.imIAccount.STATE_CONNECTING;
}
else if (aTopic == "account-connected") {
this.connectionState = Ci.imIAccount.STATE_CONNECTED;
this._finishedAutoLogin();
this.timeOfLastConnect = Date.now();
if (this.firstConnectionState != Ci.imIAccount.FIRST_CONNECTION_OK)
this.firstConnectionState = Ci.imIAccount.FIRST_CONNECTION_OK;
delete this.connectionStateMsg;
}
else if (aTopic == "account-disconnecting") {
this.connectionState = Ci.imIAccount.STATE_DISCONNECTING;
this.connectionErrorMessage = aData;
delete this.connectionStateMsg;
this._finishedAutoLogin();
let firstConnectionState = this.firstConnectionState;
if (firstConnectionState != Ci.imIAccount.FIRST_CONNECTION_OK &&
firstConnectionState != Ci.imIAccount.FIRST_CONNECTION_CRASHED)
this.firstConnectionState = Ci.imIAccount.FIRST_CONNECTION_UNKNOWN;
let connectionErrorReason = this.prplAccount.connectionErrorReason;
if (connectionErrorReason != Ci.prplIAccount.NO_ERROR) {
if (connectionErrorReason == Ci.prplIAccount.ERROR_NETWORK_ERROR ||
connectionErrorReason == Ci.prplIAccount.ERROR_ENCRYPTION_ERROR)
this._startReconnectTimer();
this._sendNotification("account-connect-error");
}
}
else if (aTopic == "account-disconnected")
this.connectionState = Ci.imIAccount.STATE_DISCONNECTED;
else
throw Cr.NS_ERROR_UNEXPECTED;
this._sendNotification(aTopic, aData);
},
_observedStatusInfo: null,
get observedStatusInfo() this._observedStatusInfo,
_statusObserver: null,
set observedStatusInfo(aUserStatusInfo) {
if (!this.prplAccount)
return;
if (this._statusObserver)
this.statusInfo.removeObserver(this._statusObserver);
this._observedStatusInfo = aUserStatusInfo;
if (!this._statusObserver) {
let prplAccount = this.prplAccount;
this._statusObserver = {
observe: function(aSubject, aTopic, aData) {
prplAccount.observe(aSubject, aTopic, aData);
}
};
}
this.statusInfo.addObserver(this._statusObserver);
},
get statusInfo() this._observedStatusInfo || Services.core.globalUserStatus,
reconnectAttempt: 0,
timeOfLastConnect: 0,
timeOfNextReconnect: 0,
_reconnectTimer: null,
_startReconnectTimer: function() {
/* If the last successful connection is older than 10 seconds, reset the
number of reconnection attemps. */
const kTimeBeforeSuccessfulConnection = 10;
if (this.timeOfLastConnect &&
this.timeOfLastConnect + kTimeBeforeSuccessfulConnection * 1000 < Date.now()) {
delete this.reconnectAttempt;
delete this.timeOfLastConnect;
}
let timers =
Services.prefs.getCharPref("messenger.accounts.reconnectTimer").split(",");
let delay = timers[Math.max(this.reconnectAttempt, timers.length - 1)];
let msDelay = parseInt(delay) * 1000;
++this.reconnectAttempt;
this.timeOfNextReconnect = Date.now() + msDelay;
this._reconnectTimer = setTimeout(this.connect.bind(this), msDelay);
},
_sendNotification: function(aTopic, aData) {
Services.obs.notifyObservers(this, aTopic, aData);
},
get firstConnectionState() {
try {
return this.prefBranch.getIntPref(kPrefAccountFirstConnectionState);
} catch (e) {
return Ci.imIAccount.FIRST_CONNECTION_OK;
}
},
set firstConnectionState(aState) {
if (aState == Ci.imIAccount.FIRST_CONNECTION_OK)
this.prefBranch.deleteBranch(kPrefAccountFirstConnectionState);
else {
this.prefBranch.setIntPref(kPrefAccountFirstConnectionState, aState);
// We want to save this pref immediately when trying to connect.
if (aState == Ci.imIAccount.FIRST_CONNECTION_PENDING)
SavePrefTimer.saveNow();
else
SavePrefTimer.initTimer();
}
},
get normalizedName() this._ensurePrplAccount.normalizedName,
_sendUpdateNotification: function() {
this._sendNotification("account-updated");
},
set alias(val) {
if (val)
this.prefBranch.setCharPref(kPrefAccountAlias, val);
else
this.prefBranch.deleteBranch(kPrefAccountAlias);
this._sendUpdateNotification();
},
get alias() {
try {
return this.prefBranch.getCharPref(kPrefAccountAlias);
} catch (e) {
return "";
}
},
get password() {
try {
return this.prefBranch.getCharPref(kPrefAccountPassword);
} catch (e) {
return "";
}
},
get _passwordRequired()
!this.protocol.noPassword && !this.protocol.passwordOptional,
set password(aPassword) {
this.prefBranch.setCharPref(kPrefAccountPassword, aPassword);
if (aPassword &&
this._connectionErrorReason == Ci.imIAccount.ERROR_MISSING_PASSWORD) {
if (this.firstConnectionState == Ci.imIAccount.FIRST_CONNECTION_CRASHED)
this._connectionErrorReason = Ci.imIAccount.ERROR_CRASHED;
else
this._connectionErrorReason = Ci.imIAccount.NO_ERROR;
}
else if (!aPassword && this._passwordRequired)
this._connectionErrorReason = Ci.imIAccount.ERROR_MISSING_PASSWORD;
this._sendUpdateNotification();
},
get autoLogin() {
let autoLogin = true;
try {
autoLogin = this.prefBranch.getBoolPref(kPrefAccountAutoLogin);
} catch (e) { }
return autoLogin;
},
set autoLogin(val) {
this.prefBranch.setBoolPref(kPrefAccountAutoLogin, val);
SavePrefTimer.initTimer();
this._sendUpdateNotification();
},
_autoLoginPending: false,
checkAutoLogin: function() {
// No auto-login if: the account has an error at the imIAccount level
// (unknown protocol, missing password, first connection crashed),
// the account is already connected or connecting, or autoLogin is off.
if (this._connectionErrorReason != Ci.prplIAccount.NO_ERROR ||
this.connecting || this.connected || !this.autoLogin)
return;
this._autoLoginPending = true;
AutoLoginCounter.startAutoLogin();
try {
this.connect();
} catch (e) {
Cu.reportError(e);
this._finishedAutoLogin();
}
},
_finishedAutoLogin: function() {
if (!this.hasOwnProperty("_autoLoginPending"))
return;
delete this._autoLoginPending;
AutoLoginCounter.finishedAutoLogin();
},
remove: function() {
this.unInit();
Services.contacts.forgetAccount(this.numericId);
this.prefBranch.deleteBranch("");
},
unInit: function() {
// remove any pending reconnection timer.
this.cancelReconnection();
// remove any pending autologin preference used for crash detection.
this._finishedAutoLogin();
// If the first connection was pending on quit, we set it back to unknown.
if (this.firstConnectionState == Ci.imIAccount.FIRST_CONNECTION_PENDING)
this.firstConnectionState = Ci.imIAccount.FIRST_CONNECTION_UNKNOWN;
// and make sure we cleanup the save pref timer.
SavePrefTimer.unInitTimer();
if (this.prplAccount)
this.prplAccount.unInit();
delete this.protocol;
delete this.prplAccount;
},
get _ensurePrplAccount() {
if (this.prplAccount)
return this.prplAccount;
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
},
connect: function() { this._ensurePrplAccount.connect(); },
disconnect: function() { this._ensurePrplAccount.disconnect(); },
get disconnected() this.connectionState == Ci.imIAccount.STATE_DISCONNECTED,
get connected() this.connectionState == Ci.imIAccount.STATE_CONNECTED,
get connecting() this.connectionState == Ci.imIAccount.STATE_CONNECTING,
get disconnecting() this.connectionState == Ci.imIAccount.STATE_DISCONNECTING,
cancelReconnection: function() {
if (this._reconnectTimer) {
clearTimeout(this._reconnectTimer);
delete this._reconnectTimer;
}
delete this.reconnectAttempt;
delete this.timeOfNextReconnect;
},
createConversation: function(aName)
this._ensurePrplAccount.createConversation(aName),
addBuddy: function(aTag, aName) {
this._ensurePrplAccount.addBuddy(aTag, aName);
},
loadBuddy: function(aBuddy, aTag)
this._ensurePrplAccount.loadBuddy(aBuddy, aTag), // FIXME for unknown proto
requestBuddyInfo: function(aBuddyName) {
this._ensurePrplAccount.requestBuddyInfo(aBuddyName);
},
getChatRoomFields: function() this._ensurePrplAccount.getChatRoomFields(),
getChatRoomDefaultFieldValues: function(aDefaultChatName)
this._ensurePrplAccount.getChatRoomDefaultFieldValues(aDefaultChatName),
get canJoinChat() this.prplAccount ? this.prplAccount.canJoinChat : false,
joinChat: function(aComponents) {
this._ensurePrplAccount.joinChat(aComponents);
},
setBool: function(aName, aVal) {
this.prefBranch.setBoolPref(kAccountOptionPrefPrefix + aName, aVal);
if (this.prplAccount)
this.prplAccount.setBool(aName, aVal);
SavePrefTimer.initTimer();
},
setInt: function(aName, aVal) {
this.prefBranch.setIntPref(kAccountOptionPrefPrefix + aName, aVal);
if (this.prplAccount)
this.prplAccount.setInt(aName, aVal);
SavePrefTimer.initTimer();
},
setString: function(aName, aVal) {
this.prefBranch.setCharPref(kAccountOptionPrefPrefix + aName, aVal);
if (this.prplAccount)
this.prplAccount.setString(aName, aVal);
SavePrefTimer.initTimer();
},
save: function() { SavePrefTimer.saveNow(); },
get HTMLEnabled() this._ensurePrplAccount.HTMLEnabled,
get noBackgroundColors() this._ensurePrplAccount.noBackgroundColors,
get autoResponses() this._ensurePrplAccount.autoResponses,
get singleFormatting() this._ensurePrplAccount.singleFormatting,
get noNewlines() this._ensurePrplAccount.noNewlines,
get noFontSizes() this._ensurePrplAccount.noFontSizes,
get noUrlDesc() this._ensurePrplAccount.noUrlDesc,
get noImages() this._ensurePrplAccount.noImages,
get maxMessageLength() this._ensurePrplAccount.maxMessageLength,
get proxyInfo() this._ensurePrplAccount.proxyInfo,
set proxyInfo(val) { this._ensurePrplAccount.proxyInfo = val; }
};
function AccountsService() { }
AccountsService.prototype = {
initAccounts: function() {
this._initAutoLoginStatus();
this._accounts = [];
this._accountsById = {};
for each (let account in this._accountList.split(",")) {
try {
account.trim();
if (!account)
throw Cr.NS_ERROR_INVALID_ARG;
let newAccount = new imAccount(account);
this._accounts.push(newAccount);
this._accountsById[newAccount.numericId] = newAccount;
} catch (e) {
Cu.reportError(e);
dump(e + " " + e.toSource() + "\n");
}
}
this._prefObserver = this.observe.bind(this);
Services.prefs.addObserver(kPrefMessengerAccounts, this._prefObserver, false);
},
_observingAccountListChange: true,
_prefObserver: null,
observe: function(aSubject, aTopic, aData) {
if (aTopic != "nsPref:changed" || aData != kPrefMessengerAccounts ||
!this._observingAccountListChange)
return;
this._accounts =
this._accountList.split(",").map(String.trim)
.filter(function (k) k.indexOf(kAccountKeyPrefix) == 0)
.map(function (k) parseInt(k.substr(kAccountKeyPrefix.length)))
.map(this.getAccountByNumericId, this)
.filter(function (a) a);
Services.obs.notifyObservers(this, "account-list-updated", null);
},
get _accountList() Services.prefs.getCharPref(kPrefMessengerAccounts),
set _accountList(aNewList) {
this._observingAccountListChange = false;
Services.prefs.setCharPref(kPrefMessengerAccounts, aNewList);
delete this._observingAccountListChange;
},
unInitAccounts: function() {
for each (let account in this._accounts)
account.unInit();
delete this._accounts;
delete this._accountsById;
Services.prefs.removeObserver(kPrefMessengerAccounts, this._prefObserver);
delete this._prefObserver;
},
autoLoginStatus: Ci.imIAccountsService.AUTOLOGIN_ENABLED,
_initAutoLoginStatus: function() {
/* If auto-login is already disabled, do nothing */
if (this.autoLoginStatus != Ci.imIAccountsService.AUTOLOGIN_ENABLED)
return;
let prefs = Services.prefs;
if (!prefs.getIntPref("messenger.startup.action")) {
// the value 0 means that we start without connecting the accounts
this.autoLoginStatus = Ci.imIAccountsService.AUTOLOGIN_USER_DISABLED;
return;
}
/* Disable auto-login if we are running in safe mode */
if (Services.appinfo.inSafeMode) {
this.autoLoginStatus = Ci.imIAccountsService.AUTOLOGIN_SAFE_MODE;
return;
}
/* If the application was started offline, disable auto-login */
if (!Services.io.manageOfflineStatus && Services.io.offline) {
this.autoLoginStatus = Ci.imIAccountsService.AUTOLOGIN_START_OFFLINE;
return;
}
/* Check if we crashed at the last startup during autologin */
let autoLoginPending;
if (prefs.getPrefType(kPrefAutologinPending) == prefs.PREF_INVALID ||
!(autoLoginPending = prefs.getIntPref(kPrefAutologinPending))) {
// if the pref isn't set, then we haven't crashed: keep autologin enabled
return;
}
// Last autologin hasn't finished properly.
// For now, assume it's because of a crash.
this.autoLoginStatus = Ci.imIAccountsService.AUTOLOGIN_CRASH;
prefs.deleteBranch(kPrefAutologinPending);
#ifdef MOZ_CRASHREPORTER
try {
// Try to get more info with breakpad
let lastCrashTime = 0;
/* Locate the LastCrash file */
let lastCrash =
Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties)
.get("UAppData", Ci.nsILocalFile);
lastCrash.append("Crash Reports");
lastCrash.append("LastCrash");
if (lastCrash.exists()) {
/* Ok, the file exists, now let's try to read it */
let is = Cc["@mozilla.org/network/file-input-stream;1"]
.createInstance(Ci.nsIFileInputStream);
let sis = Cc["@mozilla.org/scriptableinputstream;1"]
.createInstance(Ci.nsIScriptableInputStream);
is.init(lastCrash, -1, 0, 0);
sstream.init(sis);
lastCrashTime = parseInt(sstream.read(lastCrash.fileSize));
sstream.close();
fstream.close();
}
// The file not existing is totally acceptable, it just means that
// either we never crashed or breakpad is not enabled.
// In this case, lastCrashTime will keep its 0 initialization value.
/*dump("autoLoginPending = " + autoLoginPending +
", lastCrash = " + lastCrashTime +
", difference = " + lastCrashTime - autoLoginPending + "\n");*/
if (lastCrashTime < autoLoginPending) {
// the last crash caught by breakpad is older than our last autologin
// attempt.
// If breakpad is currently enabled, we can be confident that
// autologin was interrupted for an exterior reason
// (application killed by the user, power outage, ...)
try {
Services.appinfo.QueryInterface(Ci.nsICrashReporter)
.annotateCrashReport("=", "");
} catch (e) {
// This should fail with NS_ERROR_INVALID_ARG if breakpad is enabled,
// and NS_ERROR_NOT_INITIALIZED if it is not.
if (e != Cr.NS_ERROR_NOT_INITIALIZED)
this.autoLoginStatus = Ci.imIAccountsService.AUTOLOGIN_ENABLED;
}
}
} catch (e) {
// if we failed to get the last crash time, then keep the
// AUTOLOGIN_CRASH value in mAutoLoginStatus and return.
return;
}
#endif
},
processAutoLogin: function() {
if (Services.io.offline)
throw Cr.NS_ERROR_FAILURE;
for each (let account in this._accounts)
account.checkAutoLogin();
// Make sure autologin is now enabled, so that we don't display a
// message stating that it is disabled and asking the user if it
// should be processed now.
this.autoLoginStatus = Ci.imIAccountsService.AUTOLOGIN_ENABLED;
// Notify observers so that any message stating that autologin is
// disabled can be removed
Services.observers(this, "autologin-processed", null);
},
getAccountById: function(aAccountId) {
if (aAccountId.indexOf(kAccountKeyPrefix) != 0)
throw Cr.NS_ERROR_INVALID_ARG;
let id = parseInt(aAccountId.substr(kAccountKeyPrefix.length));
return this.getAccountByNumericId(id);
},
getAccountByNumericId: function(aAccountId) this._accountsById[aAccountId],
getAccounts: function() new nsSimpleEnumerator(this._accounts),
createAccount: function(aName, aPrpl) {
// Ensure an account with the same name and protocol doesn't already exist.
let prpl = Services.core.getProtocolById(aPrpl);
if (!prpl)
throw Cr.NS_ERROR_UNEXPECTED;
if (prpl.accountExists(aName)) {
Cu.reportError("Attempted to create a duplicate account!");
throw Cr.NS_ERROR_ALREADY_INITIALIZED;
}
/* First get a unique id for the new account. */
let id;
let found = true;
for (id = 1; found; ++id) {
if (this._accountsById.hasOwnProperty(id))
continue;
/* id isn't used by a known account, double check it isn't
already used in the sqlite database. This should never
happen, except if we have a corrupted profile. */
found = Services.contacts.accountIdExists(id);
if (found)
Services.console.logStringMessage("No account " + id + " but there is some data in the buddy list for an account with this number. Your profile may be corrupted.");
}
/* Actually create the new account. */
let key = kAccountKeyPrefix + id;
let account = new imAccount(key, aName, aPrpl);
/* Keep it in the local account lists. */
this._accounts.push(account);
this._accountsById[id] = account;
/* Save the account list pref. */
let list = this._accountList;
this._accountList = list ? list + "," + key : key;
Services.obs.notifyObservers(account, "account-added", null);
return account;
},
deleteAccount: function(aAccountId) {
let account = this.getAccountById(aAccountId);
if (!account)
throw Cr.NS_ERROR_INVALID_ARG;
let index = this._accounts.indexOf(account);
if (index == -1)
throw Cr.NS_ERROR_UNEXPECTED;
let id = account.numericId;
Services.obs.notifyObservers(account, "account-removed", null);
account.remove();
this._accounts.splice(index, 1);
delete this._accountsById[id];
/* Update the account list pref. */
let list = this._accountList;
this._accountList =
list.split(",").filter(function (k) k.trim() != aAccountId).join(",");
},
QueryInterface: XPCOMUtils.generateQI([Ci.imIAccountsService]),
classDescription: "Accounts",
classID: Components.ID("{a94b5427-cd8d-40cf-b47e-b67671953e70}"),
contractID: "@instantbird.org/purple/accounts-service;1"
};
const NSGetFactory = XPCOMUtils.generateNSGetFactory([AccountsService]);

Просмотреть файл

@ -0,0 +1,2 @@
component {a94b5427-cd8d-40cf-b47e-b67671953e70} imAccounts.js
contract @instantbird.org/purple/accounts-service;1 {a94b5427-cd8d-40cf-b47e-b67671953e70}

Просмотреть файл

@ -151,7 +151,7 @@ CommandsService.prototype = {
usageContext: Ci.imICommand.CONTEXT_ALL,
priority: Ci.imICommand.PRIORITY_HIGH,
run: function(aMsg) {
Services.core.setStatus(statusValue, aMsg);
Services.core.globalUserStatus.setStatus(statusValue, aMsg);
return true;
}
});

Просмотреть файл

@ -39,6 +39,76 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource:///modules/imXPCOMUtils.jsm");
Cu.import("resource:///modules/imServices.jsm");
var gDBConnection = null;
function getDBConnection()
{
const NS_APP_USER_PROFILE_50_DIR = "ProfD";
let dbFile =
Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties)
.get(NS_APP_USER_PROFILE_50_DIR, Ci.nsIFile);
dbFile.append("blist.sqlite");
let conn =
Cc["@mozilla.org/storage/service;1"].getService(Ci.mozIStorageService)
.openDatabase(dbFile);
if (!conn.connectionReady)
throw Cr.NS_ERROR_UNEXPECTED;
// Grow blist db in 512KB increments.
conn.setGrowthIncrement(512 * 1024, "");
// Create tables and indexes.
[
"CREATE TABLE IF NOT EXISTS accounts (" +
"id INTEGER PRIMARY KEY, " +
"name VARCHAR, " +
"prpl VARCHAR)",
"CREATE TABLE IF NOT EXISTS contacts (" +
"id INTEGER PRIMARY KEY, " +
"firstname VARCHAR, " +
"lastname VARCHAR, " +
"alias VARCHAR)",
"CREATE TABLE IF NOT EXISTS buddies (" +
"id INTEGER PRIMARY KEY, " +
"key VARCHAR NOT NULL, " +
"name VARCHAR NOT NULL, " +
"srv_alias VARCHAR, " +
"position INTEGER, " +
"icon BLOB, " +
"contact_id INTEGER)",
"CREATE INDEX IF NOT EXISTS buddies_contactindex " +
"ON buddies (contact_id)",
"CREATE TABLE IF NOT EXISTS tags (" +
"id INTEGER PRIMARY KEY, " +
"name VARCHAR UNIQUE NOT NULL, " +
"position INTEGER)",
"CREATE TABLE IF NOT EXISTS contact_tag (" +
"contact_id INTEGER NOT NULL, " +
"tag_id INTEGER NOT NULL)",
"CREATE INDEX IF NOT EXISTS contact_tag_contactindex " +
"ON contact_tag (contact_id)",
"CREATE INDEX IF NOT EXISTS contact_tag_tagindex " +
"ON contact_tag (tag_id)",
"CREATE TABLE IF NOT EXISTS account_buddy (" +
"account_id INTEGER NOT NULL, " +
"buddy_id INTEGER NOT NULL, " +
"status VARCHAR, " +
"tag_id INTEGER)",
"CREATE INDEX IF NOT EXISTS account_buddy_accountindex " +
"ON account_buddy (account_id)",
"CREATE INDEX IF NOT EXISTS account_buddy_buddyindex " +
"ON account_buddy (buddy_id)"
].forEach(conn.executeSimpleSQL);
return conn;
}
// Wrap all the usage of DBConn inside a transaction that will be
// commited automatically at the end of the event loop spin so that
// we flush buddy list data to disk only once per event loop spin.
@ -47,30 +117,17 @@ this.__defineGetter__("DBConn", function() {
if (gDBConnWithPendingTransaction)
return gDBConnWithPendingTransaction;
let conn = Services.core.storageConnection;
gDBConnWithPendingTransaction = conn;
conn.beginTransaction();
if (!gDBConnection)
gDBConnection = getDBConnection();
gDBConnWithPendingTransaction = gDBConnection;
gDBConnection.beginTransaction();
executeSoon(function() {
gDBConnWithPendingTransaction.commitTransaction();
gDBConnWithPendingTransaction = null;
});
return conn;
return gDBConnection;
});
var AccountsById = { };
function getAccountById(aId) {
if (AccountsById.hasOwnProperty(aId))
return AccountsById[aId];
let account = null;
try {
account = Services.core.getAccountByNumericId(aId);
} catch (x) { /* Not found */ }
AccountsById[aId] = account;
return account;
}
function TagsService() { }
TagsService.prototype = {
get wrappedJSObject() this,
@ -1154,7 +1211,8 @@ ContactsService.prototype = {
statement = DBConn.createStatement("SELECT account_id, buddy_id, tag_id FROM account_buddy");
while (statement.executeStep()) {
let account = getAccountById(statement.getInt32(0));
let account =
Services.accounts.getAccountByNumericId(statement.getInt32(0));
let buddy = BuddiesById[statement.getInt32(1)];
let tag = TagsById[statement.getInt32(2)];
try {
@ -1163,7 +1221,7 @@ ContactsService.prototype = {
buddy._addAccount(ab, tag);
} catch (e) {
// FIXME ab shouldn't be NULL (once purpleAccount is finished)
// It currently doesn't work write with unknown protocols.
// It currently doesn't work right with unknown protocols.
Components.utils.reportError(e);
dump(e + "\n");
}
@ -1172,7 +1230,6 @@ ContactsService.prototype = {
otherContactsTag._initHiddenTags();
},
unInitContacts: function() {
AccountsById = { };
Tags = [];
TagsById = { };
// Avoid shutdown leaks caused by references to native components
@ -1266,6 +1323,45 @@ ContactsService.prototype = {
ContactsById[buddy.contact.id]._moved(aOldTag, aNewTag);
},
storeAccount: function(aId, aUserName, aPrplId) {
let statement =
DBConn.createStatement("SELECT name, prpl FROM accounts WHERE id = :id");
statement.params.id = aId;
if (statement.executeStep()) {
if (statement.getUTF8String(0) == aUserName &&
statement.getUTF8String(1) == aPrplId)
return; // The account is already stored correctly.
throw Cr.NS_ERROR_UNEXPECTED; // Corrupted database?!?
}
// Actually store the account.
statement = DBConn.createStatement("INSERT INTO accounts (id, name, prpl) " +
"VALUES(:id, :userName, :prplId)");
statement.params.id = aId;
statement.params.userName = aUserName;
statement.params.prplId = aPrplId;
statement.execute();
},
accountIdExists: function(aId) {
let statement =
DBConn.createStatement("SELECT id FROM accounts WHERE id = :id");
statement.params.id = aId;
return statement.executeStep();
},
forgetAccount: function(aId) {
let statement =
DBConn.createStatement("DELETE FROM accounts WHERE id = :accountId");
statement.params.accountId = aId;
statement.execute();
// removing the account from the accounts table is not enought,
// we need to remove all the associated account_buddy entries too
statement = DBConn.createStatement("DELETE FROM account_buddy " +
"WHERE account_id = :accountId");
statement.params.accountId = aId;
statement.execute();
},
QueryInterface: XPCOMUtils.generateQI([Ci.imIContactsService]),
classDescription: "Contacts",
classID: Components.ID("{8c3725dd-ee26-489d-8135-736015af8c7f}"),

Просмотреть файл

@ -132,6 +132,7 @@ UIConversation.prototype = {
else
aContactId.value = 0;
delete this._currentTargetId;
this.notifyObservers(this, "ui-conversation-closed");
Services.obs.notifyObservers(this, "ui-conversation-closed", null);
return true;
@ -165,6 +166,9 @@ UIConversation.prototype = {
return this._messages;
},
checkClose: function() {
if (!this._currentTargetId)
return true; // already closed.
if (!Services.prefs.getBoolPref("messenger.conversations.alwaysClose") &&
(this.isChat && !this.left ||
!this.isChat && this.unreadIncomingMessageCount != 0))
@ -287,6 +291,7 @@ UIConversation.prototype = {
close: function() {
for each (let conv in this._purpleConv)
conv.close();
delete this._currentTargetId;
this.notifyObservers(this, "ui-conversation-closed");
Services.obs.notifyObservers(this, "ui-conversation-closed", null);
},

Просмотреть файл

@ -0,0 +1,368 @@
/* ***** 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 the Instantbird messenging client, released
* 2011.
*
* The Initial Developer of the Original Code is
* Florian QUEZE <florian@instantbird.org>.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* 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 ***** */
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource:///modules/imServices.jsm");
Cu.import("resource:///modules/imXPCOMUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "categoryManager",
"@mozilla.org/categorymanager;1",
"nsICategoryManager");
const kQuitApplicationGranted = "quit-application-granted";
const kProtocolPluginCategory = "im-protocol-plugin";
const kPrefReportIdle = "messenger.status.reportIdle";
const kPrefUserIconFilename = "messenger.status.userIconFileName";
const kPrefUserDisplayname = "messenger.status.userDisplayName";
const kPrefTimeBeforeIdle = "messenger.status.timeBeforeIdle";
const kPrefAwayWhenIdle = "messenger.status.awayWhenIdle";
const kPrefDefaultMessage = "messenger.status.defaultIdleAwayMessage";
const NS_IOSERVICE_GOING_OFFLINE_TOPIC = "network:offline-about-to-go-offline";
const NS_IOSERVICE_OFFLINE_STATUS_TOPIC = "network:offline-status-changed";
function UserStatus()
{
this._observers = [];
if (Services.prefs.getBoolPref(kPrefReportIdle))
this._addIdleObserver();
Services.prefs.addObserver(kPrefReportIdle, this, false);
if (Services.io.offline)
this._offline = true;
Services.obs.addObserver(this, NS_IOSERVICE_GOING_OFFLINE_TOPIC, false);
Services.obs.addObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, false);
}
UserStatus.prototype = {
__proto__: ClassInfo("imIUserStatusInfo", "User status info"),
unInit: function() {
this._observers = [];
Services.prefs.removeObserver(kPrefReportIdle, this);
if (this._observingIdleness)
this._removeIdleObserver();
Services.obs.removeObserver(this, NS_IOSERVICE_GOING_OFFLINE_TOPIC);
Services.obs.removeObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
},
_observingIdleness: false,
_addIdleObserver: function() {
this._observingIdleness = true;
this._idleService =
Cc["@mozilla.org/widget/idleservice;1"].getService(Ci.nsIIdleService);
Services.obs.addObserver(this, "im-sent", false);
this._timeBeforeIdle = Services.prefs.getIntPref(kPrefTimeBeforeIdle);
if (this._timeBeforeIdle < 0)
this._timeBeforeIdle = 0;
Services.prefs.addObserver(kPrefTimeBeforeIdle, this, false);
if (this._timeBeforeIdle)
this._idleService.addIdleObserver(this, this._timeBeforeIdle);
},
_removeIdleObserver: function() {
if (this._timeBeforeIdle)
this._idleService.removeIdleObserver(this, this._timeBeforeIdle);
Services.prefs.removeObserver(kPrefTimeBeforeIdle, this);
delete this._timeBeforeIdle;
Services.obs.removeObserver(this, "im-sent");
delete this._idleService;
delete this._observingIdleness;
},
observe: function(aSubject, aTopic, aData) {
if (aTopic == "nsPref:changed") {
if (aData == kPrefReportIdle) {
let reportIdle = Services.prefs.getBoolPref(kPrefReportIdle);
if (reportIdle && !this._observingIdleness)
this._addIdleObserver();
else if (!reportIdle && this._observingIdleness)
this._removeIdleObserver();
}
else if (aData == kPrefTimeBeforeIdle) {
let timeBeforeIdle = Services.prefs.getIntPref(kPrefTimeBeforeIdle);
if (timeBeforeIdle != this._timeBeforeIdle) {
if (this._timeBeforeIdle)
this._idleService.removeIdleObserver(this, this._timeBeforeIdle);
this._timeBeforeIdle = timeBeforeIdle;
if (this._timeBeforeIdle)
this._idleService.addIdleObserver(this, this._timeBeforeIdle);
}
}
else
throw Cr.NS_ERROR_UNEXPECTED;
}
else if (aTopic == NS_IOSERVICE_GOING_OFFLINE_TOPIC)
this._offline = true;
else if (aTopic == NS_IOSERVICE_OFFLINE_STATUS_TOPIC && aData == "online")
this._offline = false;
else
this._checkIdle();
},
_offlineStatusType: Ci.imIStatusInfo.STATUS_AVAILABLE,
set offline(aOffline) {
let statusType = this.statusType;
let statusText = this.statusText;
if (aOffline)
this._offlineStatusType = Ci.imIStatusInfo.STATUS_OFFLINE;
else
delete this._offlineStatusType;
if (this.statusType != statusType || this.statusText != statusText)
this._notifyObservers("status-changed", this.statusText);
},
_idleTime: 0,
get idleTime() this._idleTime,
set idleTime(aIdleTime) {
this._idleTime = aIdleTime;
this._notifyObservers("idle-time-changed", aIdleTime);
},
_idle: false,
_idleStatusText: "",
_idleStatusType: Ci.imIStatusInfo.STATUS_AVAILABLE,
_checkIdle: function() {
let idleTime = this._idleService.idleTime / 1000;
let idle = this._timeBeforeIdle && idleTime >= this._timeBeforeIdle;
if (idle == this._idle)
return;
let statusType = this.statusType;
let statusText = this.statusText;
this._idle = idle;
if (idle) {
this.idleTime = idleTime;
if (Services.prefs.getBoolPref(kPrefAwayWhenIdle)) {
this._idleStatusType = Ci.imIStatusInfo.STATUS_AWAY;
this._idleStatusText =
Services.prefs.getComplexValue(kPrefDefaultMessage,
Ci.nsIPrefLocalizedString).data;
}
}
else {
this.idleTime = 0;
delete this._idleStatusType;
delete this._idleStatusText;
}
if (this.statusType != statusType || this.statusText != statusText)
this._notifyObservers("status-changed", this.statusText);
},
_statusText: "",
get statusText() this._statusText || this._idleStatusText,
_statusType: Ci.imIStatusInfo.STATUS_AVAILABLE,
get statusType() Math.min(this._statusType, this._idleStatusType, this._offlineStatusType),
setStatus: function(aStatus, aMessage) {
if (aStatus != Ci.imIStatusInfo.STATUS_UNKNOWN)
this._statusType = aStatus;
if (aStatus != Ci.imIStatusInfo.STATUS_OFFLINE)
this._statusText = aMessage;
this._notifyObservers("status-changed", aMessage);
},
_getProfileDir: function()
Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties)
.get("ProfD" /*NS_APP_USER_PROFILE_50_DIR*/, Ci.nsIFile),
setUserIcon: function(aIconFile) {
let folder = this._getProfileDir();
let newName = "";
if (aIconFile) {
// Get the extension (remove trailing dots - invalid Windows extension).
let ext = aIconFile.leafName.replace(/.*(\.[a-z0-9]+)\.*/i, "$1");
// newName = userIcon-<timestamp(now)>.<aIconFile.extension>
newName = "userIcon-" + (Date.now() / 1000) + ext;
// Copy the new icon file to newName in the profile folder.
aIconFile.copyTo(folder, newName);
}
// Get the previous file name before saving the new file name.
let oldFileName = Services.prefs.getCharPref(kPrefUserIconFilename);
Services.prefs.setCharPref(kPrefUserIconFilename, newName);
// Now that the new icon has been copied to the profile directory
// and the pref value changed, we can remove the old icon. Ignore
// failures so that we always fire the user-icon-changed notification.
try {
if (oldFileName) {
folder.append(oldFileName);
if (folder.exists())
folder.remove(false);
}
} catch (e) {
Cu.reportError(e);
}
this._notifyObservers("user-icon-changed", newName);
},
getUserIcon: function() {
let filename = Services.prefs.getCharPref(kPrefUserIconFilename);
if (!filename)
return null; // No icon has been set.
let file = this._getProfileDir();
file.append(filename);
if (!file.exists()) {
Services.console.logStringMessage("Invalid userIconFileName preference");
return null;
}
return Services.io.newFileURI(file);
},
get displayName() Services.prefs.getCharPref(kPrefUserDisplayname),
set displayName(aDisplayName) {
Services.prefs.setCharPref(kPrefUserDisplayname, aDisplayName);
this._notifyObservers("user-display-name-changed", aDisplayName);
},
addObserver: function(aObserver) {
if (this._observers.indexOf(aObserver) == -1)
this._observers.push(aObserver);
},
removeObserver: function(aObserver) {
this._observers = this._observers.filter(function(o) o !== aObserver);
},
_notifyObservers: function(aTopic, aData) {
for each (let observer in this._observers)
observer.observe(this, aTopic, aData);
}
};
var gCoreService;
function CoreService() { gCoreService = this; }
CoreService.prototype = {
_initialized: false,
globalUserStatus: null,
init: function() {
if (this._initialized)
throw Cr.NS_ERROR_ALREADY_INITIALIZED;
Services.obs.addObserver(this, kQuitApplicationGranted, false);
this._initialized = true;
Services.cmd.initCommands();
this._protos = {};
this.globalUserStatus = new UserStatus();
this.globalUserStatus.addObserver({
observe: function(aSubject, aTopic, aData) {
Services.obs.notifyObservers(aSubject, aTopic, aData);
}
});
Services.accounts.initAccounts();
Services.contacts.initContacts();
Services.conversations.initConversations();
},
observe: function(aObject, aTopic, aData) {
if (aTopic == kQuitApplicationGranted)
this.quit();
},
quit: function() {
if (!this._initialized)
throw Cr.NS_ERROR_NOT_INITIALIZED;
Services.obs.removeObserver(this, kQuitApplicationGranted);
Services.obs.notifyObservers(this, "prpl-quit", null);
Services.conversations.unInitConversations();
Services.accounts.unInitAccounts();
Services.contacts.unInitContacts();
Services.cmd.unInitCommands();
this.globalUserStatus.unInit();
delete this.globalUserStatus;
delete this._protos;
delete this._initialized;
},
getProtocols: function() {
if (!this._initialized)
throw Cr.NS_ERROR_NOT_INITIALIZED;
let protocols = [];
let entries = categoryManager.enumerateCategory(kProtocolPluginCategory);
while (entries.hasMoreElements()) {
let id = entries.getNext().QueryInterface(Ci.nsISupportsCString).data;
let proto = this.getProtocolById(id);
if (proto)
protocols.push(proto);
}
return new nsSimpleEnumerator(protocols);
},
getProtocolById: function(aPrplId) {
if (!this._initialized)
throw Cr.NS_ERROR_NOT_INITIALIZED;
if (this._protos.hasOwnProperty(aPrplId))
return this._protos[aPrplId];
let cid;
try {
cid = categoryManager.getCategoryEntry(kProtocolPluginCategory, aPrplId);
} catch (e) {
return null; // no protocol registered for this id.
}
let proto = null;
try {
proto = Cc[cid].createInstance(Ci.prplIProtocol);
} catch (e) {
// This is a real error, the protocol is registered and failed to init.
Cu.reportError(e);
}
if (!proto)
return null;
proto.init(aPrplId);
this._protos[aPrplId] = proto;
return proto;
},
QueryInterface: XPCOMUtils.generateQI([Ci.imICoreService]),
classDescription: "Core",
classID: Components.ID("{073f5953-853c-4a38-bd81-255510c31c2e}"),
contractID: "@instantbird.org/purple/core-service;1"
};
const NSGetFactory = XPCOMUtils.generateNSGetFactory([CoreService]);

Просмотреть файл

@ -0,0 +1,2 @@
component {073f5953-853c-4a38-bd81-255510c31c2e} imCore.js
contract @instantbird.org/purple/core-service;1 {073f5953-853c-4a38-bd81-255510c31c2e}

Просмотреть файл

@ -40,9 +40,12 @@ let EXPORTED_SYMBOLS = ["Services"];
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(Services, "accounts",
"@instantbird.org/purple/accounts-service;1",
"imIAccountsService");
XPCOMUtils.defineLazyServiceGetter(Services, "core",
"@instantbird.org/purple/core;1",
"purpleICoreService");
"@instantbird.org/purple/core-service;1",
"imICoreService");
XPCOMUtils.defineLazyServiceGetter(Services, "cmd",
"@instantbird.org/purple/commands-service;1",
"imICommandsService");

Просмотреть файл

@ -431,7 +431,7 @@ const messageReplacements = {
// For outgoing messages, use the current user icon.
if (aMsg.outgoing) {
iconURL = Services.core.getUserIcon();
iconURL = aMsg.conversation.account.statusInfo.getUserIcon();
if (iconURL)
return iconURL.spec;
}

Просмотреть файл

@ -127,6 +127,11 @@ function ClassInfo(aInterfaces, aDescription)
if (!Array.isArray(aInterfaces))
aInterfaces = [aInterfaces];
for each (let i in aInterfaces)
if (typeof i == "string" && !(i in Ci))
Services.console.logStringMessage("ClassInfo: unknown interface " + i);
this._interfaces =
aInterfaces.map(function (i) typeof i == "string" ? Ci[i] : i);

Просмотреть файл

@ -58,26 +58,18 @@ initLogModule("jsProtoHelper");
function normalize(aString) aString.replace(/[^a-z0-9]/gi, "").toLowerCase()
XPCOMUtils.defineLazyGetter(this, "AccountBase", function()
Components.Constructor("@instantbird.org/purple/account;1",
"purpleIAccountBase")
);
const ForwardAccountPrototype = {
__proto__: ClassInfo("purpleIAccount", "generic account object"),
_init: function _init(aProtoInstance, aBase) {
__proto__: ClassInfo("prplIAccount", "generic account object"),
_init: function _init(aBase) {
this._base = aBase;
this._base.concreteAccount = this;
this._protocol = aProtoInstance;
},
get base() this._base.purpleIAccountBase,
checkAutoLogin: function() this._base.checkAutoLogin(),
remove: function() this._base.remove(),
UnInit: function() this._base.UnInit(),
observe: function(aSubject, aTopic, aData) {
this._base.observe(aSubject, aTopic, aData);
},
unInit: function() this._base.unInit(),
connect: function() this._base.connect(),
disconnect: function() this._base.disconnect(),
cancelReconnection: function() this._base.cancelReconnection(),
createConversation: function(aName) this._base.createConversation(aName),
addBuddy: function(aTag, aName) this._base.addBuddy(aTag, aName),
loadBuddy: function(aBuddy, aTag) this._base.loadBuddy(aBuddy, aTag),
@ -89,33 +81,11 @@ const ForwardAccountPrototype = {
setBool: function(aName, aVal) this._base.setBool(aName, aVal),
setInt: function(aName, aVal) this._base.setInt(aName, aVal),
setString: function(aName, aVal) this._base.setString(aName, aVal),
save: function() this._base.save(),
// grep attribute purpleIAccount.idl |sed 's/.* //;s/;//;s/\(.*\)/ get \1() this._base.\1,/'
// Exception: the protocol getter is handled locally.
get canJoinChat() this._base.canJoinChat,
get name() this._base.name,
get normalizedName() this._base.normalizedName,
get id() this._base.id,
get numericId() this._base.numericId,
get protocol() this._protocol,
get autoLogin() this._base.autoLogin,
get firstConnectionState() this._base.firstConnectionState,
get password() this._base.password,
get rememberPassword() this._base.rememberPassword,
get alias() this._base.alias,
get proxyInfo() this._base.proxyInfo,
get connectionStateMsg() this._base.connectionStateMsg,
get connectionErrorReason() this._base.connectionErrorReason,
get reconnectAttempt() this._base.reconnectAttempt,
get timeOfNextReconnect() this._base.timeOfNextReconnect,
get timeOfLastConnect() this._base.timeOfLastConnect,
get connectionErrorMessage() this._base.connectionErrorMessage,
get connectionState() this._base.connectionState,
get disconnected() this._base.disconnected,
get connected() this._base.connected,
get connecting() this._base.connecting,
get disconnecting() this._base.disconnecting,
get HTMLEnabled() this._base.HTMLEnabled,
get noBackgroundColors() this._base.noBackgroundColors,
get autoResponses() this._base.autoResponses,
@ -126,20 +96,48 @@ const ForwardAccountPrototype = {
get noImages() this._base.noImages,
get maxMessageLength() this._base.maxMessageLength,
// grep attribute purpleIAccount.idl |grep -v readonly |sed 's/.* //;s/;//;s/\(.*\)/ set \1(val) { this._base.\1 = val; },/'
set autoLogin(val) { this._base.autoLogin = val; },
set firstConnectionState(val) { this._base.firstConnectionState = val; },
set password(val) { this._base.password = val; },
set rememberPassword(val) { this._base.rememberPassword = val; },
set alias(val) { this._base.alias = val; },
set proxyInfo(val) { this._base.proxyInfo = val; }
};
const GenericAccountPrototype = {
__proto__: ForwardAccountPrototype,
_init: function _init(aProtoInstance, aKey, aName) {
ForwardAccountPrototype._init.call(this, aProtoInstance, new AccountBase());
this._base.init(aKey, aName, aProtoInstance);
__proto__: ClassInfo("prplIAccount", "generic account object"),
_init: function _init(aProtocol, aImAccount) {
this.protocol = aProtocol;
this.imAccount = aImAccount;
},
observe: function(aSubject, aTopic, aData) {},
unInit: function() {},
connect: function() { throw Cr.NS_ERROR_NOT_IMPLEMENTED; },
disconnect: function() { throw Cr.NS_ERROR_NOT_IMPLEMENTED; },
createConversation: function(aName) { throw Cr.NS_ERROR_NOT_IMPLEMENTED; },
joinChat: function(aComponents) { throw Cr.NS_ERROR_NOT_IMPLEMENTED; },
setBool: function(aName, aVal) {},
setInt: function(aName, aVal) {},
setString: function(aName, aVal) {},
get name() this.imAccount.name,
get connected() this.imAccount.connected,
get connecting() this.imAccount.connecting,
get disconnected() this.imAccount.disconnected,
get disconnecting() this.imAccount.disconnecting,
_connectionErrorReason: Ci.prplIAccount.NO_ERROR,
get connectionErrorReason() this._connectionErrorReason,
reportConnected: function() {
this.imAccount.observe(this, "account-connected", null);
},
reportConnecting: function(aConnectionStateMsg) {
if (!this.connecting)
this.imAccount.observe(this, "account-connecting", null);
if (aConnectionStateMsg)
this.imAccount.observe(this, "account-connect-progress", aConnectionStateMsg);
},
reportDisconnected: function() {
this.imAccount.observe(this, "account-disconnected", null);
},
reportDisconnecting: function(aConnectionErrorReason, aConnectionErrorMessage) {
this._connectionErrorReason = aConnectionErrorReason;
this.imAccount.observe(this, "account-disconnecting", aConnectionErrorMessage);
},
addBuddy: function(aTag, aName) {
@ -148,13 +146,14 @@ const GenericAccountPrototype = {
},
loadBuddy: function(aBuddy, aTag) {
try {
return new AccountBuddy(this, aBuddy, aTag) ;
return new AccountBuddy(this, aBuddy, aTag);
} catch (x) {
dump(x + "\n");
return null;
}
},
requestBuddyInfo: function(aBuddyName) {},
get canJoinChat() false,
getChatRoomFields: function() {
if (!this.chatRoomFields)
return EmptyEnumerator;
@ -190,12 +189,22 @@ const GenericAccountPrototype = {
getBool: function(aName) this.getPref(aName, "Bool"),
get prefs() this._prefs ||
(this._prefs = Services.prefs.getBranch("messenger.account." + this.id +
".options.")),
(this._prefs = Services.prefs.getBranch("messenger.account." +
this.imAccount.id + ".options.")),
get normalizedName() normalize(this.name),
get proxyInfo() { throw Cr.NS_ERROR_NOT_IMPLEMENTED; },
set proxyInfo(val) { throw Cr.NS_ERROR_NOT_IMPLEMENTED; }
set proxyInfo(val) { throw Cr.NS_ERROR_NOT_IMPLEMENTED; },
get HTMLEnabled() true,
get noBackgroundColors() true,
get autoResponses() false,
get singleFormatting() false,
get noNewlines() false,
get noFontSizes() false,
get noUrlDesc() false,
get noImages() true,
get maxMessageLength() 0
};
@ -206,7 +215,7 @@ const GenericAccountBuddyPrototype = {
throw "aUserName is required when aBuddy is null";
this._tag = aTag;
this._account = aAccount;
this._account = aAccount.imAccount;
this._buddy = aBuddy;
this._userName = aUserName;
},
@ -371,7 +380,7 @@ const GenericConversationPrototype = {
flags: Ci.nsIClassInfo.DOM_OBJECT,
_init: function(aAccount, aName) {
this.account = aAccount;
this.account = aAccount.imAccount;
this._name = aName;
this._observers = [];
Services.conversations.addConversation(this);
@ -597,8 +606,12 @@ ChatRoomFieldValues.prototype = {
// the name getter needs to be implemented
const GenericProtocolPrototype = {
__proto__: ClassInfo("purpleIProtocol", "Generic protocol object"),
__proto__: ClassInfo("prplIProtocol", "Generic protocol object"),
init: function(aId) {
if (aId != this.id)
throw NS_ERROR_NOT_IMPLEMENTED;
},
get id() "prpl-" + this.normalizedName,
get normalizedName() normalize(this.name),
get iconBaseURI() "chrome://instantbird/skin/prpl-generic/",
@ -671,9 +684,9 @@ const GenericProtocolPrototype = {
get contractID() "@instantbird.org/purple/" + this.normalizedName + ";1"
};
function ForwardAccount(aProtocol, aBaseAccount)
function ForwardAccount(aBaseAccount)
{
this._init(aProtocol, aBaseAccount);
this._init(aBaseAccount);
}
ForwardAccount.prototype = ForwardAccountPrototype;
@ -687,8 +700,8 @@ const ForwardProtocolPrototype = {
this._base = Services.core.getProtocolById(this.baseId);
return this._base;
},
getAccount: function(aKey, aName)
new ForwardAccount(this, this.base.getAccount(aKey, aName)),
getAccount: function(aImAccount)
new ForwardAccount(this.base.getAccount(aImAccount)),
get iconBaseURI() this.base.iconBaseURI,
getOptions: function() this.base.getOptions(),

Просмотреть файл

@ -66,9 +66,9 @@ Conversation.prototype = {
};
Conversation.prototype.__proto__ = GenericConvIMPrototype;
function Account(aProtoInstance, aKey, aName)
function Account(aProtoInstance, aImAccount)
{
this._init(aProtoInstance, aKey, aName);
this._init(aProtoInstance, aImAccount);
}
Account.prototype = {
connect: function() {
@ -118,7 +118,7 @@ jsTestProtocol.prototype = {
{label: "Server", separator: "@", defaultValue: "default.server",
reverse: true}
],
getAccount: function(aKey, aName) new Account(this, aKey, aName),
getAccount: function(aImAccount) new Account(this, aImAccount),
classID: Components.ID("{a0774c5a-4aea-458b-9fbc-8d3cbf1a4630}"),
};
jsTestProtocol.prototype.__proto__ = GenericProtocolPrototype;

Просмотреть файл

@ -1,6 +1,6 @@
component {a0774c5a-4aea-458b-9fbc-8d3cbf1a4630} jsTestProtocol.js
contract @instantbird.org/purple/jstest;1 {a0774c5a-4aea-458b-9fbc-8d3cbf1a4630}
category js-protocol-plugin @instantbird.org/purple/jstest;1 @instantbird.org/purple/jstest;1
category im-protocol-plugin prpl-jstest @instantbird.org/purple/jstest;1
component {88795348-8a4b-4018-890d-5314cb08ec4d} jsTestProtocol.js
contract @instantbird.org/purple/override;1 {88795348-8a4b-4018-890d-5314cb08ec4d}
category js-protocol-plugin @instantbird.org/purple/override;1 @instantbird.org/purple/override;1
category im-protocol-plugin prpl-override @instantbird.org/purple/override;1

Просмотреть файл

@ -61,10 +61,10 @@ facebookProtocol.prototype = {
get iconBaseURI() "chrome://prpl-facebook/skin/",
get baseId() "prpl-jabber",
getAccount: function(aKey, aName) {
let account = ForwardProtocolPrototype.getAccount.call(this, aKey, aName);
getAccount: function(aImAccount) {
let account = ForwardProtocolPrototype.getAccount.call(this, aImAccount);
account.__defineGetter__("canJoinChat", function() false);
account.setString("connection_security", "opportunistic_tls");
aImAccount.setString("connection_security", "opportunistic_tls");
return account;
},
getOptions: function() EmptyEnumerator,

Просмотреть файл

@ -50,11 +50,11 @@ gtalkProtocol.prototype = {
get iconBaseURI() "chrome://prpl-gtalk/skin/",
get baseId() "prpl-jabber",
getAccount: function(aKey, aName) {
let account = ForwardProtocolPrototype.getAccount.call(this, aKey, aName);
getAccount: function(aImAccount) {
let account = ForwardProtocolPrototype.getAccount.call(this, aImAccount);
let connectServer =
/@g(oogle)?mail.com(\/|$)/.test(aName) ? "" : "talk.google.com";
account.setString("connect_server", connectServer);
/@g(oogle)?mail.com(\/|$)/.test(aImAccount.name) ? "" : "talk.google.com";
aImAccount.setString("connect_server", connectServer);
return account;
},
getOptions: function() EmptyEnumerator,

Просмотреть файл

@ -1,6 +1,6 @@
component {ad8a6454-2f5a-40c2-86ca-30062408125e} gtalkOverrideProtocol.js
contract @instantbird.org/purple/gtalk;1 {ad8a6454-2f5a-40c2-86ca-30062408125e}
category js-protocol-plugin @instantbird.org/purple/gtalk;1 @instantbird.org/purple/gtalk;1
category im-protocol-plugin prpl-gtalk @instantbird.org/purple/gtalk;1
component {61bc3528-df53-4481-a61a-74c3a2e8c9fd} facebookOverrideProtocol.js
contract @instantbird.org/purple/facebook;1 {61bc3528-df53-4481-a61a-74c3a2e8c9fd}
category js-protocol-plugin @instantbird.org/purple/facebook;1 @instantbird.org/purple/facebook;1
category im-protocol-plugin prpl-facebook @instantbird.org/purple/facebook;1

Просмотреть файл

@ -318,13 +318,11 @@ Conversation.prototype = {
get nick() "@" + this.account.name
};
function Account(aProtoInstance, aKey, aName)
function Account(aProtocol, aImAccount)
{
this._init(aProtoInstance, aKey, aName);
this._init(aProtocol, aImAccount);
this._knownMessageIds = {};
this._userInfo = {};
Services.obs.addObserver(this, "status-changed", false);
}
Account.prototype = {
__proto__: GenericAccountPrototype,
@ -352,7 +350,7 @@ Account.prototype = {
if (this.connected || this.connecting)
return;
this.base.connecting();
this.reportConnecting();
this._enabled = true;
// Read the OAuth token from the prefs
@ -380,12 +378,15 @@ Account.prototype = {
this.getTimelines();
},
// Currently only used for "status-changed" notification.
observe: function(aSubject, aTopic, aMsg) {
// Currently only used for "status-changed" notification.
if (aTopic != "status-changed")
return;
if (!this._enabled)
return;
let statusType = aSubject.currentStatusType;
let statusType = aSubject.statusType;
if (statusType == Ci.imIStatusInfo.STATUS_OFFLINE) {
// This will remove the _enabled value...
this.disconnect();
@ -516,8 +517,7 @@ Account.prototype = {
},
getTimelines: function() {
this.base
.connecting(_("connection.requestTimelines"));
this.reportConnecting(_("connection.requestTimelines"));
// If we have a last known message ID, append it as a get parameter.
let lastMsgParam = this.prefs.prefHasUserValue("lastMessageId") ?
@ -644,7 +644,7 @@ Account.prototype = {
if (this._timelineAuthError)
delete this._timelineAuthError;
this.base.connected();
this.reportConnected();
// If the conversation already exists, notify it we are back online.
if (this._timeline)
@ -686,7 +686,7 @@ Account.prototype = {
},
onStreamError: function(aError) {
delete this._streamingRequest;
this.gotDisconnected(this._base.ERROR_NETWORK_ERROR, aError);
this.gotDisconnected(Ci.prplIAccount.ERROR_NETWORK_ERROR, aError);
},
onDataAvailable: function(aRequest) {
let text = aRequest.target.responseText;
@ -734,7 +734,7 @@ Account.prototype = {
},
requestToken: function() {
this.base.connecting(_("connection.initAuth"));
this.reportConnecting(_("connection.initAuth"));
let oauthParams =
[["oauth_callback", encodeURIComponent(this.completionURI)]];
this.signAndSend("oauth/request_token", null, [],
@ -746,7 +746,7 @@ Account.prototype = {
let data = this._parseURLData(aData);
if (!data.oauth_callback_confirmed ||
!data.oauth_token || !data.oauth_token_secret) {
this.gotDisconnected(this._base.ERROR_OTHER_ERROR,
this.gotDisconnected(Ci.prplIAccount.ERROR_OTHER_ERROR,
_("connection.failedToken"));
return;
}
@ -756,7 +756,7 @@ Account.prototype = {
this.requestAuthorization();
},
requestAuthorization: function() {
this.base.connecting(_("connection.requestAuth"));
this.reportConnecting(_("connection.requestAuth"));
const url = this.baseURI + "oauth/authorize?oauth_token=";
this._browserRequest = {
get promptText() _("authPrompt"),
@ -768,7 +768,7 @@ Account.prototype = {
return;
this.account
.gotDisconnected(this.account._base.ERROR_AUTHENTICATION_FAILED,
.gotDisconnected(Ci.prplIAccount.ERROR_AUTHENTICATION_FAILED,
_("connection.error.authCancelled"));
},
loaded: function(aWindow, aWebProgress) {
@ -825,14 +825,14 @@ Account.prototype = {
onAuthorizationReceived: function(aData) {
let data = this._parseURLData(aData.split("?")[1]);
if (data.oauth_token != this.token || !data.oauth_verifier) {
this.gotDisconnected(this._base.ERROR_OTHER_ERROR,
this.gotDisconnected(Ci.prplIAccount.ERROR_OTHER_ERROR,
_("connection.error.authFailed"));
return;
}
this.requestAccessToken(data.oauth_verifier);
},
requestAccessToken: function(aTokenVerifier) {
this.base.connecting(_("connection.requestAccess"));
this.reportConnecting(_("connection.requestAccess"));
this.signAndSend("oauth/access_token", null, [],
this.onAccessTokenReceived, this.onError, this,
[["oauth_verifier", aTokenVerifier]]);
@ -878,16 +878,16 @@ Account.prototype = {
return;
if (aError === undefined)
aError = this._base.NO_ERROR;
aError = Ci.prplIAccount.NO_ERROR;
let connected = this.connected;
this.base.disconnecting(aError, aErrorMessage);
this.reportDisconnecting(aError, aErrorMessage);
this.cleanUp();
if (this._timeline && connected)
this._timeline.notifyObservers(this._timeline, "update-conv-chatleft");
delete this._enabled;
this.base.disconnected();
this.reportDisconnected();
},
UnInit: function() {
unInit: function() {
this.cleanUp();
// If we've received any messages, update the last known message.
let newestMessageId = "";
@ -900,8 +900,6 @@ Account.prototype = {
}
if (newestMessageId)
this.prefs.setCharPref("lastMessageId", newestMessageId);
Services.obs.removeObserver(this, "status-changed");
this._base.UnInit();
},
disconnect: function() {
this.gotDisconnected();
@ -909,11 +907,11 @@ Account.prototype = {
onError: function(aException) {
if (aException == "offline") {
this.gotDisconnected(this._base.ERROR_NETWORK_ERROR,
this.gotDisconnected(Ci.prplIAccount.ERROR_NETWORK_ERROR,
_("connection.error.noNetwork"));
}
else
this.gotDisconnected(this._base.ERROR_OTHER_ERROR, aException.toString());
this.gotDisconnected(Ci.prplIAccount.ERROR_OTHER_ERROR, aException.toString());
},
onRequestedInfoReceived: function(aData) {
@ -987,7 +985,7 @@ TwitterProtocol.prototype = {
options: {
"track": {get label() _("options.track"), default: ""}
},
getAccount: function(aKey, aName) new Account(this, aKey, aName),
getAccount: function(aImAccount) new Account(this, aImAccount),
classID: Components.ID("{31082ff6-1de8-422b-ab60-ca0ac0b2af13}")
};

Просмотреть файл

@ -1,3 +1,3 @@
component {31082ff6-1de8-422b-ab60-ca0ac0b2af13} twitter.js
contract @instantbird.org/purple/twitter;1 {31082ff6-1de8-422b-ab60-ca0ac0b2af13}
category js-protocol-plugin @instantbird.org/purple/twitter;1 @instantbird.org/purple/twitter;1
category im-protocol-plugin prpl-twitter @instantbird.org/purple/twitter;1

Просмотреть файл

@ -37,6 +37,7 @@
* ***** END LICENSE BLOCK ***** */
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource:///modules/imServices.jsm");
Cu.import("resource:///modules/imXPCOMUtils.jsm");
Cu.import("resource:///modules/ibCore.jsm");
@ -51,9 +52,8 @@ ibCommandLineHandler.prototype = {
}
if (cmdLine.handleFlag("n", false)) {
Components.classes["@instantbird.org/purple/core;1"]
.getService(Ci.purpleICoreService)
.autoLoginStatus = Ci.purpleICoreService.AUTOLOGIN_USER_DISABLED;
Services.accounts.autoLoginStatus =
Ci.imIAccountsService.AUTOLOGIN_USER_DISABLED;
}
// Initialize the core only at the first real startup,

Просмотреть файл

@ -60,8 +60,8 @@ StatusCLH.prototype = {
cmdLine.removeArguments(statusIndex, statusIndex + 1);
// We're keeping the old status message here.
Services.core.setStatus(Status.toFlag(statusParam),
Services.core.currentStatusMessage);
let us = Services.core.globalUserStatus;
us.setStatus(Status.toFlag(statusParam), us.statusText);
// Only perform the default action (i.e. loading the buddy list) if
// Instantbird is launched with a status flag.

Просмотреть файл

@ -128,8 +128,8 @@ var gMinTrayR = {
},
setStatus: function MinTrayR_setStatus(aStatusParam) {
Services.core.setStatus(Status.toFlag(aStatusParam),
Services.core.currentStatusMessage);
let us = Services.core.globalUserStatus;
us.setStatus(Status.toFlag(aStatusParam), us.statusText);
}
};

Просмотреть файл

@ -82,7 +82,7 @@
versionField.value = versionField.value + xai.platformVersion
+ ' (' + xai.platformBuildID + ')';
var pcs = Components.classes["@instantbird.org/purple/core;1"]
var pcs = Components.classes["@instantbird.org/libpurple/core;1"]
.getService(Components.interfaces.purpleICoreService)
versionField = document.getElementById("libpurpleVersionField");
versionField.value = versionField.value + pcs.version;

Просмотреть файл

@ -38,7 +38,7 @@
const autoJoinPref = "autoJoin";
const events = [
"purple-quit"
"prpl-quit"
];
var account = {
@ -94,7 +94,7 @@ var account = {
removeObservers(account, events);
},
observe: function account_observe(aObject, aTopic, aData) {
if (aTopic == "purple-quit") {
if (aTopic == "prpl-quit") {
// libpurple is being uninitialized. Close this dialog.
window.close();
}
@ -106,7 +106,8 @@ var account = {
var proxy;
var result;
if (type == Ci.purpleIProxyInfo.useGlobal) {
proxy = Services.core.globalProxy;
proxy = Cc["@instantbird.org/libpurple/core;1"]
.getService(Ci.purpleICoreService).globalProxy;
type = proxy.type;
}
else
@ -322,7 +323,7 @@ var account = {
if (connectionInfoHasChanged) {
/* This is the first time we try to connect with these parameters */
this.account.firstConnectionState = this.account.FIRST_CONNECTION_SET;
this.account.firstConnectionState = this.account.FIRST_CONNECTION_UNKNOWN;
if (this.account.connecting) {
this.account.disconnect();
@ -331,10 +332,10 @@ var account = {
}
let errorReason = this.account.connectionErrorReason;
if (this.account.disconnected &&
errorReason != Ci.purpleIAccount.NO_ERROR &&
errorReason != Ci.purpleIAccount.ERROR_MISSING_PASSWORD &&
errorReason != Ci.purpleIAccount.ERROR_CRASHED &&
errorReason != Ci.purpleIAccount.ERROR_UNKNOWN_PRPL) {
errorReason != Ci.prplIAccount.NO_ERROR &&
errorReason != Ci.imIAccount.ERROR_MISSING_PASSWORD &&
errorReason != Ci.imIAccount.ERROR_CRASHED &&
errorReason != Ci.imIAccount.ERROR_UNKNOWN_PRPL) {
this.account.connect();
}
}

Просмотреть файл

@ -95,7 +95,7 @@
this.refreshConnectedLabel();
} else if (this._account.disconnected) {
state = "disconnected";
if (this._account.connectionErrorReason != Ci.purpleIAccount.NO_ERROR)
if (this._account.connectionErrorReason != Ci.prplIAccount.NO_ERROR)
this.updateConnectionError();
else
this.removeAttribute("error");
@ -136,12 +136,12 @@
var account = this._account;
var text;
let errorReason = account.connectionErrorReason;
if (errorReason == Ci.purpleIAccount.ERROR_UNKNOWN_PRPL)
if (errorReason == Ci.imIAccount.ERROR_UNKNOWN_PRPL)
text = bundle.getFormattedString(key + "UnknownPrpl",
[account.protocol.id]);
else if (errorReason == Ci.purpleIAccount.ERROR_MISSING_PASSWORD)
else if (errorReason == Ci.imIAccount.ERROR_MISSING_PASSWORD)
text = bundle.getString(key + "MissingPassword");
else if (errorReason == Ci.purpleIAccount.ERROR_CRASHED)
else if (errorReason == Ci.imIAccount.ERROR_CRASHED)
text = bundle.getString(key + "CrashedAccount");
else
text = account.connectionErrorMessage;
@ -230,7 +230,7 @@
this._connectedLabel = null;
if (this._account.connected)
this.refreshConnectedLabel();
if (this._account.connectionErrorReason == Ci.purpleIAccount.NO_ERROR)
if (this._account.connectionErrorReason == Ci.prplIAccount.NO_ERROR)
this.updateConnectionState();
else
this.updateConnectionError();

Просмотреть файл

@ -39,7 +39,7 @@
const PREF_EXTENSIONS_GETMOREPROTOCOLSURL = "extensions.getMoreProtocolsURL";
const events = [
"purple-quit"
"prpl-quit"
];
var accountWizard = {
@ -70,7 +70,7 @@ var accountWizard = {
removeObservers(accountWizard, events);
},
observe: function am_observe(aObject, aTopic, aData) {
if (aTopic == "purple-quit") {
if (aTopic == "prpl-quit") {
// libpurple is being uninitialized. We can't create any new
// account so keeping this wizard open would be pointless, close it.
window.close();
@ -222,7 +222,8 @@ var accountWizard = {
var proxy;
var result;
if (type == Ci.purpleIProxyInfo.useGlobal) {
proxy = Services.core.globalProxy;
proxy = Cc["@instantbird.org/libpurple/core;1"]
.getService(Ci.purpleICoreService).globalProxy;
type = proxy.type;
}
else
@ -438,11 +439,9 @@ var accountWizard = {
},
createAccount: function aw_createAccount() {
var acc = Services.core.createAccount(this.username, this.proto.id);
if (!this.proto.noPassword) {
var acc = Services.accounts.createAccount(this.username, this.proto.id);
if (!this.proto.noPassword)
acc.password = this.password;
acc.rememberPassword = true;
}
if (this.alias)
acc.alias = this.alias;
//FIXME: newMailNotification

Просмотреть файл

@ -40,7 +40,7 @@ Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
// This is the list of notifications that the account manager window observes
const events = [
"purple-quit",
"prpl-quit",
"account-list-updated",
"account-added",
"account-updated",
@ -126,7 +126,7 @@ var gAccountManager = {
this.disableCommandItems();
},
observe: function am_observe(aObject, aTopic, aData) {
if (aTopic == "purple-quit") {
if (aTopic == "prpl-quit") {
// libpurple is being uninitialized. We don't need the account
// manager window anymore, close it.
this.close();
@ -144,7 +144,7 @@ var gAccountManager = {
return;
}
else if (aTopic == "status-changed") {
this.setOffline(aObject.currentStatusType == Ci.imIStatusInfo.STATUS_OFFLINE);
this.setOffline(aObject.statusType == Ci.imIStatusInfo.STATUS_OFFLINE);
return;
}
else if (aTopic == "account-list-updated") {
@ -152,8 +152,8 @@ var gAccountManager = {
return;
}
if (!(aObject instanceof Ci.purpleIAccount))
throw "Bad notification.";
// The following notification handlers need an account.
aObject.QueryInterface(Ci.imIAccount);
if (aTopic == "account-added") {
document.getElementById("accountsDesk").selectedIndex = 1;
@ -203,6 +203,9 @@ var gAccountManager = {
};
if (aTopic in stateEvents) {
let elt = document.getElementById(aObject.id);
if (!elt)
return; // probably disconnecting a removed account.
if (aTopic == "account-connecting") {
elt.removeAttribute("error");
elt.updateConnectionState();
@ -283,7 +286,7 @@ var gAccountManager = {
Services.prefs.setBoolPref("messenger.accounts.promptOnDelete", false);
}
Services.core.deleteAccount(selectedItem.id);
Services.accounts.deleteAccount(selectedItem.id);
},
new: function am_new() {
this.openDialog("chrome://instantbird/content/accountWizard.xul");
@ -326,8 +329,8 @@ var gAccountManager = {
let isCommandDisabled =
(this.isOffline ||
(account.disconnected &&
(account.connectionErrorReason == Ci.purpleIAccount.ERROR_UNKNOWN_PRPL ||
account.connectionErrorReason == Ci.purpleIAccount.ERROR_MISSING_PASSWORD)));
(account.connectionErrorReason == Ci.imIAccount.ERROR_UNKNOWN_PRPL ||
account.connectionErrorReason == Ci.imIAccount.ERROR_MISSING_PASSWORD)));
[[activeCommandElt, isCommandDisabled],
[document.getElementById("cmd_moveup"), accountList.selectedIndex == 0],
@ -446,7 +449,7 @@ var gAccountManager = {
Services.prefs.setCharPref("messenger.accounts", array.join(","));
},
getAccounts: function am_getAccounts() getIter(Services.core.getAccounts()),
getAccounts: function am_getAccounts() getIter(Services.accounts.getAccounts()),
openDialog: function am_openDialog(aUrl, aArgs) {
this.modalDialog = true;
@ -454,17 +457,17 @@ var gAccountManager = {
this.modalDialog = false;
},
setAutoLoginNotification: function am_setAutoLoginNotification() {
var pcs = Services.core;
var autoLoginStatus = pcs.autoLoginStatus;
var as = Services.accounts;
var autoLoginStatus = as.autoLoginStatus;
let isOffline = false;
let crashCount = 0;
for (let acc in this.getAccounts())
if (acc.autoLogin && acc.firstConnectionState == acc.FIRST_CONNECTION_CRASHED)
++crashCount;
if (autoLoginStatus == pcs.AUTOLOGIN_ENABLED && crashCount == 0) {
this.setOffline(isOffline ||
pcs.currentStatusType == Ci.imIStatusInfo.STATUS_OFFLINE);
if (autoLoginStatus == as.AUTOLOGIN_ENABLED && crashCount == 0) {
let status = Services.core.globalUserStatus.statusType;
this.setOffline(isOffline || status == Ci.imIStatusInfo.STATUS_OFFLINE);
return;
}
@ -479,27 +482,27 @@ var gAccountManager = {
var label;
switch (autoLoginStatus) {
case pcs.AUTOLOGIN_USER_DISABLED:
case as.AUTOLOGIN_USER_DISABLED:
label = bundle.getString("accountsManager.notification.userDisabled.label");
break;
case pcs.AUTOLOGIN_SAFE_MODE:
case as.AUTOLOGIN_SAFE_MODE:
label = bundle.getString("accountsManager.notification.safeMode.label");
break;
case pcs.AUTOLOGIN_START_OFFLINE:
case as.AUTOLOGIN_START_OFFLINE:
label = bundle.getString("accountsManager.notification.startOffline.label");
isOffline = true;
break;
case pcs.AUTOLOGIN_CRASH:
case as.AUTOLOGIN_CRASH:
label = bundle.getString("accountsManager.notification.crash.label");
priority = box.PRIORITY_WARNING_MEDIUM;
break;
/* One or more accounts made the application crash during their connection.
If none, this function has already returned */
case pcs.AUTOLOGIN_ENABLED:
case as.AUTOLOGIN_ENABLED:
if (!("PluralForm" in window))
Components.utils.import("resource://gre/modules/PluralForm.jsm");
label = bundle.getString("accountsManager.notification.singleCrash.label");
@ -511,8 +514,8 @@ var gAccountManager = {
default:
label = bundle.getString("accountsManager.notification.other.label");
}
this.setOffline(isOffline ||
pcs.currentStatusType == Ci.imIStatusInfo.STATUS_OFFLINE);
let status = Services.core.globalUserStatus.statusType;
this.setOffline(isOffline || status == Ci.imIStatusInfo.STATUS_OFFLINE);
box.appendNotification(label, "autologinStatus", null, priority, [connectNowButton]);
},
@ -523,7 +526,7 @@ var gAccountManager = {
ioService.offline = false;
}
Services.core.processAutoLogin();
Services.accounts.processAutoLogin();
gAccountManager.accountList.selectedItem.buttons.setFocus();
},

Просмотреть файл

@ -44,7 +44,7 @@ var addBuddy = {
buildAccountList: function ab_buildAccountList() {
var accountList = document.getElementById("accountlist");
for (let acc in getIter(Services.core.getAccounts())) {
for (let acc in getIter(Services.accounts.getAccounts())) {
if (!acc.connected)
continue;
var proto = acc.protocol;
@ -76,7 +76,7 @@ var addBuddy = {
getValue: function ab_getValue(aId) document.getElementById(aId).value,
create: function ab_create() {
var account = Services.core.getAccountById(this.getValue("accountlist"));
var account = Services.accounts.getAccountById(this.getValue("accountlist"));
var name = this.getValue("name");
var tag;

Просмотреть файл

@ -48,7 +48,7 @@ const events = ["contact-availability-changed",
"ui-conversation-hidden",
"user-display-name-changed",
"user-icon-changed",
"purple-quit"];
"prpl-quit"];
const showOfflineBuddiesPref = "messenger.buddies.showOffline";
@ -313,7 +313,7 @@ buddyListContextMenu.prototype = {
var buddyList = {
observe: function bl_observe(aSubject, aTopic, aMsg) {
if (aTopic == "purple-quit") {
if (aTopic == "prpl-quit") {
window.close();
return;
}
@ -400,12 +400,12 @@ var buddyList = {
},
displayUserIcon: function bl_displayUserIcon() {
let icon = Services.core.getUserIcon();
let icon = Services.core.globalUserStatus.getUserIcon();
document.getElementById("userIcon").src = icon ? icon.spec : "";
},
displayUserDisplayName: function bl_displayUserDisplayName() {
let displayName = Services.core.userDisplayName;
let displayName = Services.core.globalUserStatus.displayName;
let elt = document.getElementById("displayName");
if (displayName)
elt.removeAttribute("usingDefault");
@ -428,9 +428,9 @@ var buddyList = {
},
displayCurrentStatus: function bl_displayCurrentStatus() {
let pcs = Services.core;
let status = Status.toAttribute(pcs.currentStatusType);
let message = status == "offline" ? "" : pcs.currentStatusMessage;
let us = Services.core.globalUserStatus;
let status = Status.toAttribute(us.statusType);
let message = status == "offline" ? "" : us.statusText;
let statusString = this.displayStatusType(status);
let statusMessage = document.getElementById("statusMessage");
if (message)
@ -446,7 +446,7 @@ var buddyList = {
editStatus: function bl_editStatus(aEvent) {
let status = aEvent.originalTarget.getAttribute("status");
if (status == "offline")
Services.core.setStatus(Ci.imIStatusInfo.STATUS_OFFLINE, "");
Services.core.globalUserStatus.setStatus(Ci.imIStatusInfo.STATUS_OFFLINE, "");
else if (status)
this.startEditStatus(status);
},
@ -476,7 +476,7 @@ var buddyList = {
if (elt.hasAttribute("usingDefault")) {
if ("_statusTypeBeforeEditing" in this &&
this._statusTypeBeforeEditing == "offline")
elt.setAttribute("value", Services.core.currentStatusMessage);
elt.setAttribute("value", Services.core.globalUserStatus.statusMessage);
else
elt.removeAttribute("value");
}
@ -540,7 +540,7 @@ var buddyList = {
// apply the new status only if it is different from the current one
if (newStatus != Ci.imIStatusInfo.STATUS_UNKNOWN ||
elt.value != elt.getAttribute("value"))
Services.core.setStatus(newStatus, elt.value);
Services.core.globalUserStatus.setStatus(newStatus, elt.value);
}
else if ("_statusTypeBeforeEditing" in this) {
this.displayStatusType(this._statusTypeBeforeEditing);
@ -565,7 +565,7 @@ var buddyList = {
nsIFilePicker.modeOpen);
fp.appendFilters(nsIFilePicker.filterImages);
if (fp.show() == nsIFilePicker.returnOK)
Services.core.setUserIcon(fp.file);
Services.core.globalUserStatus.setUserIcon(fp.file);
},
displayNameClick: function bl_displayNameClick() {
@ -617,7 +617,7 @@ var buddyList = {
let elt = document.getElementById("displayName");
// Apply the new display name only if it is different from the current one.
if (aSave && elt.value != elt.getAttribute("value"))
Services.core.userDisplayName = elt.value;
Services.core.globalUserStatus.displayName = elt.value;
else if (elt.hasAttribute("usingDefault"))
elt.setAttribute("value", elt.getAttribute("usingDefault"));

Просмотреть файл

@ -66,12 +66,11 @@ function debug_enumerateProtocols()
function debug_connectAccount(aProto, aName, aPassword)
{
var pcs = Services.core;
var proto = pcs.getProtocolById(aProto);
var proto = Services.core.getProtocolById(aProto);
if (!proto)
throw "Couldn't get protocol " + aProto;
var acc = pcs.createAccount(aName, proto);
var acc = Services.accounts.createAccount(aName, proto);
acc.password = aPassword;
dump("trying to connect to " + proto.name +
" (" + proto.id + ") with " + aName + "\n");
@ -82,7 +81,7 @@ function debug_dumpBuddyList()
{
let formatBuddy = (function(buddy) " " + buddy.name + "\n " + buddy.getAccounts().map(function(a) a.name).join(" "));
let formatGroup = (function(aGroup) " Group " + aGroup.id + ": " + aGroup.name + "\n" + aGroup.getBuddies().map(formatBuddy).join("\n"));
dump("Buddy list:\n\n" + Services.core.getTags().map(formatGroup).join("\n\n") + "\n\n");
dump("Buddy list:\n\n" + Services.tags.getTags().map(formatGroup).join("\n\n") + "\n\n");
}
function dumpStack(offset, max_depth)

Просмотреть файл

@ -303,12 +303,12 @@ var fake = {
new Message("Florian", "Thanks :)",
{time: makeDate("10:44:12"), incoming: true, conversation: chat});
Services.core.userDisplayName = "Tom Smith";
Services.core.globalUserStatus.displayName = "Tom Smith";
// Ugly :-(
document.getElementById("userIcon").src = ib_icon_url;
},
deleteAccounts: function f_deleteAccounts() {
if (!Services.core.getAccounts().hasMoreElements())
if (!Services.accounts.getAccounts().hasMoreElements())
return;
var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
@ -317,8 +317,8 @@ var fake = {
"You are about to delete " + nbaccounts + " accounts. Are you sure?"))
throw "user aborted the operation";
for (let acc in getIter(Services.core.getAccounts()))
Services.core.deleteAccount(acc.id);
for (let acc in getIter(Services.accounts.getAccounts()))
Services.accounts.deleteAccount(acc.id);
}
};
@ -334,7 +334,7 @@ function Account(aName, aProto)
dump("account " + aName + " created\n");
}
Account.prototype = {
__proto__: ClassInfo("purpleIAccount", "generic account object"),
__proto__: ClassInfo("imIAccount", "generic account object"),
protocol: null,
password: "",
autoLogin: true,

Просмотреть файл

@ -174,5 +174,5 @@ var joinChat = {
return true;
},
getAccounts: function jc_getAccounts() getIter(Services.core.getAccounts())
getAccounts: function jc_getAccounts() getIter(Services.accounts.getAccounts())
};

Просмотреть файл

@ -50,7 +50,7 @@ var menus = {
supportsCommand: function(aCmd)
aCmd == "cmd_addbuddy" || aCmd == "cmd_joinchat",
isCommandEnabled: function(aCmd) {
let enumerator = Services.core.getAccounts();
let enumerator = Services.accounts.getAccounts();
while (enumerator.hasMoreElements()) {
let acc = enumerator.getNext();
if (acc.connected && (aCmd == "cmd_addbuddy" || acc.canJoinChat))
@ -176,7 +176,7 @@ var menus = {
checkCurrentStatusType: function menu_checkCurrentStatusType(aItems) {
if (!("Status" in window))
Components.utils.import("resource:///modules/imStatusUtils.jsm");
let status = Status.toAttribute(Services.core.currentStatusType);
let status = Status.toAttribute(Services.core.globalUserStatus.statusType);
if (status == "away")
status = "unavailable";
@ -206,8 +206,8 @@ var menus = {
blist.buddyList.startEditStatus(status);
}
else {
Services.core.setStatus(Status.toFlag(status),
Services.core.currentStatusMessage);
let us = Services.core.globalUserStatus;
us.setStatus(Status.toFlag(status), us.statusText);
}
}
};

Просмотреть файл

@ -42,7 +42,8 @@ var gProxies = {
// see what the global settings are
// build a list of existing proxies
var pcs = Services.core;
var pcs = Cc["@instantbird.org/libpurple/core;1"]
.getService(Ci.purpleICoreService);
var proxyInfoCtr = Components.Constructor("@instantbird.org/purple/proxyinfo;1",
"purpleIProxyInfo");
@ -152,7 +153,8 @@ var gProxies = {
},
accept: function proxy_accept() {
var pcs = Services.core;
var pcs = Cc["@instantbird.org/libpurple/core;1"]
.getService(Ci.purpleICoreService);
var promptService = Services.prompt;
var item = document.getElementById("proxylist").selectedItem;
if (item.id == "newProxy") {

Просмотреть файл

@ -2,11 +2,9 @@ startupFailure.title=Instantbird - Start up failure
startupFailure.apologize=Instantbird encountered a serious error and cannot start, we apologize for the inconvenience.
startupFailure.update=An updated version will probably be available shortly to fix the problem.
startupFailure.purplexpcomFileError=Description: The file "purplexpcom.xpt" is missing or corrupted.
startupFailure.xpcomRegistrationError=Description: XPCOM registration of the purplexpcom component failed.
startupFailure.purplexpcomInitError=An exception occurred while initializing purplexpcom: %S
startupFailure.libpurpleError=An error occurred while checking whether libpurple is usable.
startupFailure.noProtocolLoaded=Description: No protocol plugin could be loaded.
startupFailure.purplexpcomFileError=Description: The file "instantbird.xpt" is missing or corrupted.
startupFailure.xpcomRegistrationError=Description: XPCOM registration of the core component failed.
startupFailure.purplexpcomInitError=An exception occurred while initializing the core component: %S
startupFailure.buttonUpdate=Check for Updates
startupFailure.buttonClose=Close Instantbird

Просмотреть файл

@ -70,32 +70,19 @@ var Core = {
return false;
}
if (!Components.classes["@instantbird.org/purple/core;1"]) {
if (!Components.classes["@instantbird.org/purple/core-service;1"]) {
this._promptError("startupFailure.xpcomRegistrationError");
return false;
}
try {
var pcs = Services.core;
pcs.init();
Services.core.init();
}
catch (e) {
this._promptError("startupFailure.purplexpcomInitError", e);
return false;
}
if (!pcs.version) {
this._promptError("startupFailure.libpurpleError");
return false;
}
if (!pcs.getProtocols().hasMoreElements()) {
this._promptError("startupFailure.noProtocolLoaded");
this.uninitPurpleCore();
return false;
}
Services.conversations.initConversations();
Conversations.init();
Notifications.init();
Sounds.init();
@ -170,7 +157,7 @@ var Core = {
while (aEnumerator.hasMoreElements())
yield aEnumerator.getNext();
},
getAccounts: function() this.getIter(Services.core.getAccounts()),
getAccounts: function() this.getIter(Services.accounts.getAccounts()),
/* This function pops up the account manager if no account is
* connected or connecting.
@ -180,7 +167,7 @@ var Core = {
_showAccountManagerIfNeeded: function (aIsStarting) {
// If the current status is offline, we don't need the account manager
let isOffline =
Services.core.currentStatusType == Ci.imIStatusInfo.STATUS_OFFLINE;
Services.core.globalUserStatus.statusType == Ci.imIStatusInfo.STATUS_OFFLINE;
if (isOffline && !aIsStarting)
return;
@ -206,7 +193,7 @@ var Core = {
observe: function(aSubject, aTopic, aMsg) {
if (aTopic == "account-connected") {
let account = aSubject.QueryInterface(Components.interfaces.purpleIAccount);
let account = aSubject.QueryInterface(Ci.imIAccount);
if (!account.canJoinChat)
return;
@ -225,7 +212,7 @@ var Core = {
}
if (aTopic == "account-disconnected") {
let account = aSubject.QueryInterface(Ci.purpleIAccount);
let account = aSubject.QueryInterface(Ci.imIAccount);
if (account.reconnectAttempt <= 1)
this._showAccountManagerIfNeeded(false);
return;

Просмотреть файл

@ -37,14 +37,14 @@
#include "nsISupports.idl"
interface purpleIAccount;
interface imIAccount;
interface nsIDOMWindowInternal;
interface nsIWebProgress;
[scriptable, uuid(b89dbb38-0de4-11e0-b3d0-0002e304243c)]
interface purpleIRequestBrowser: nsISupports {
readonly attribute AUTF8String promptText;
readonly attribute purpleIAccount account;
readonly attribute imIAccount account;
readonly attribute AUTF8String url;
void cancelled();
void loaded(in nsIDOMWindowInternal aWindow,