1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5const EXPORTED_SYMBOLS = ["ircHandlers"];
6
7var ircHandlers = {
8  /*
9   * Object to hold the IRC handlers, each handler is an object that implements:
10   *   name        The display name of the handler.
11   *   priority    The priority of the handler (0 is default, positive is
12   *               higher priority)
13   *   isEnabled   A function where 'this' is bound to the account object. This
14   *               should reflect whether this handler should be used for this
15   *               account.
16   *   commands    An object of commands, each command is a function which
17   *               accepts a message object and has 'this' bound to the account
18   *               object. It should return whether the message was successfully
19   *               handler or not.
20   */
21  _ircHandlers: [],
22  // Object to hold the ISUPPORT handlers, expects the same fields as
23  // _ircHandlers.
24  _isupportHandlers: [],
25  // Object to hold the Client Capabilities handlers, expects the same fields as
26  // _ircHandlers.
27  _capHandlers: [],
28  // Object to hold the CTCP handlers, expects the same fields as _ircHandlers.
29  _ctcpHandlers: [],
30  // Object to hold the DCC handlers, expects the same fields as _ircHandlers.
31  _dccHandlers: [],
32  // Object to hold the Services handlers, expects the same fields as
33  // _ircHandlers.
34  _servicesHandlers: [],
35  // Object to hold irc message tag handlers, expects the same fields as
36  // _ircHandlers.
37  _tagHandlers: [],
38
39  _registerHandler(aArray, aHandler) {
40    // Protect ourselves from adding broken handlers.
41    if (!("commands" in aHandler)) {
42      Cu.reportError(
43        new Error(
44          'IRC handlers must have a "commands" property: ' + aHandler.name
45        )
46      );
47      return false;
48    }
49    if (!("isEnabled" in aHandler)) {
50      Cu.reportError(
51        new Error(
52          'IRC handlers must have a "isEnabled" property: ' + aHandler.name
53        )
54      );
55      return false;
56    }
57
58    aArray.push(aHandler);
59    aArray.sort((a, b) => b.priority - a.priority);
60    return true;
61  },
62
63  _unregisterHandler(aArray, aHandler) {
64    return aArray.filter(h => h.name != aHandler.name);
65  },
66
67  registerHandler(aHandler) {
68    return this._registerHandler(this._ircHandlers, aHandler);
69  },
70  unregisterHandler(aHandler) {
71    this._ircHandlers = this._unregisterHandler(this._ircHandlers, aHandler);
72  },
73
74  registerISUPPORTHandler(aHandler) {
75    return this._registerHandler(this._isupportHandlers, aHandler);
76  },
77  unregisterISUPPORTHandler(aHandler) {
78    this._isupportHandlers = this._unregisterHandler(
79      this._isupportHandlers,
80      aHandler
81    );
82  },
83
84  registerCAPHandler(aHandler) {
85    return this._registerHandler(this._capHandlers, aHandler);
86  },
87  unregisterCAPHandler(aHandler) {
88    this._capHandlers = this._unregisterHandler(this._capHandlers, aHandler);
89  },
90
91  registerCTCPHandler(aHandler) {
92    return this._registerHandler(this._ctcpHandlers, aHandler);
93  },
94  unregisterCTCPHandler(aHandler) {
95    this._ctcpHandlers = this._unregisterHandler(this._ctcpHandlers, aHandler);
96  },
97
98  registerDCCHandler(aHandler) {
99    return this._registerHandler(this._dccHandlers, aHandler);
100  },
101  unregisterDCCHandler(aHandler) {
102    this._dccHandlers = this._unregisterHandler(this._dccHandlers, aHandler);
103  },
104
105  registerServicesHandler(aHandler) {
106    return this._registerHandler(this._servicesHandlers, aHandler);
107  },
108  unregisterServicesHandler(aHandler) {
109    this._servicesHandlers = this._unregisterHandler(
110      this._servicesHandlers,
111      aHandler
112    );
113  },
114
115  registerTagHandler(aHandler) {
116    return this._registerHandler(this._tagHandlers, aHandler);
117  },
118  unregisterTagHandler(aHandler) {
119    this._tagHandlers = this._unregisterHandler(this._tagHandlers, aHandler);
120  },
121
122  // Handle a message based on a set of handlers.
123  _handleMessage(aHandlers, aAccount, aMessage, aCommand) {
124    // Loop over each handler and run the command until one handles the message.
125    for (let handler of aHandlers) {
126      try {
127        // Attempt to execute the command, by checking if the handler has the
128        // command.
129        // Parse the command with the JavaScript account object as "this".
130        if (
131          handler.isEnabled.call(aAccount) &&
132          aCommand in handler.commands &&
133          handler.commands[aCommand].call(aAccount, aMessage)
134        ) {
135          return true;
136        }
137      } catch (e) {
138        // We want to catch an error here because one of our handlers are
139        // broken, if we don't catch the error, the whole IRC plug-in will die.
140        aAccount.ERROR(
141          "Error running command " +
142            aCommand +
143            " with handler " +
144            handler.name +
145            ":\n" +
146            JSON.stringify(aMessage),
147          e
148        );
149      }
150    }
151
152    return false;
153  },
154
155  handleMessage(aAccount, aMessage) {
156    return this._handleMessage(
157      this._ircHandlers,
158      aAccount,
159      aMessage,
160      aMessage.command.toUpperCase()
161    );
162  },
163
164  handleISUPPORTMessage(aAccount, aMessage) {
165    return this._handleMessage(
166      this._isupportHandlers,
167      aAccount,
168      aMessage,
169      aMessage.isupport.parameter
170    );
171  },
172
173  handleCAPMessage(aAccount, aMessage) {
174    return this._handleMessage(
175      this._capHandlers,
176      aAccount,
177      aMessage,
178      aMessage.cap.parameter
179    );
180  },
181
182  // aMessage is a CTCP Message, which inherits from an IRC Message.
183  handleCTCPMessage(aAccount, aMessage) {
184    return this._handleMessage(
185      this._ctcpHandlers,
186      aAccount,
187      aMessage,
188      aMessage.ctcp.command
189    );
190  },
191
192  // aMessage is a DCC Message, which inherits from a CTCP Message.
193  handleDCCMessage(aAccount, aMessage) {
194    return this._handleMessage(
195      this._dccHandlers,
196      aAccount,
197      aMessage,
198      aMessage.ctcp.dcc.type
199    );
200  },
201
202  // aMessage is a Services Message.
203  handleServicesMessage(aAccount, aMessage) {
204    return this._handleMessage(
205      this._servicesHandlers,
206      aAccount,
207      aMessage,
208      aMessage.serviceName
209    );
210  },
211
212  // aMessage is a Tag Message.
213  handleTag(aAccount, aMessage) {
214    return this._handleMessage(
215      this._tagHandlers,
216      aAccount,
217      aMessage,
218      aMessage.tagName
219    );
220  },
221
222  // Checking if handlers exist.
223  get hasHandlers() {
224    return this._ircHandlers.length > 0;
225  },
226  get hasISUPPORTHandlers() {
227    return this._isupportHandlers.length > 0;
228  },
229  get hasCAPHandlers() {
230    return this._capHandlers.length > 0;
231  },
232  get hasCTCPHandlers() {
233    return this._ctcpHandlers.length > 0;
234  },
235  get hasDCCHandlers() {
236    return this._dccHandlers.length > 0;
237  },
238  get hasServicesHandlers() {
239    return this._servicesHandlers.length > 0;
240  },
241  get hasTagHandlers() {
242    return this._tagHandlers.length > 0;
243  },
244
245  // Some constant priorities.
246  get LOW_PRIORITY() {
247    return -100;
248  },
249  get DEFAULT_PRIORITY() {
250    return 0;
251  },
252  get HIGH_PRIORITY() {
253    return 100;
254  },
255};
256