]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - lib/rbot/rfc2812.rb
More outdated message in en translations
[user/henk/code/ruby/rbot.git] / lib / rbot / rfc2812.rb
1 #-- vim:sw=2:et
2 #++
3 #
4 # :title: RFC 2821 Client Protocol module
5 #
6 # This module defines the Irc::Client class, a class that can handle and
7 # dispatch messages based on RFC 2821 (Internet Relay Chat: Client Protocol)
8
9 module Irc
10   # - The server sends Replies 001 to 004 to a user upon
11   #   successful registration.
12
13   # "Welcome to the Internet Relay Network
14   # <nick>!<user>@<host>"
15   #
16   RPL_WELCOME=001
17
18   # "Your host is <servername>, running version <ver>"
19   RPL_YOURHOST=002
20
21   # "This server was created <date>"
22   RPL_CREATED=003
23
24   # "<servername> <version> <available user modes> <available channel modes>"
25   RPL_MYINFO=004
26
27   # "005 nick PREFIX=(ov)@+ CHANTYPES=#& :are supported by this server"
28   #
29   # defines the capabilities supported by the server.
30   #
31   # Previous RFCs defined message 005 as follows:
32   #
33   # - Sent by the server to a user to suggest an alternative
34   #   server.  This is often used when the connection is
35   #   refused because the server is already full.
36   #
37   # # "Try server <server name>, port <port number>"
38   #
39   # RPL_BOUNCE=005
40   #
41   RPL_ISUPPORT=005
42
43   # ":*1<reply> *( " " <reply> )"
44   #
45   # - Reply format used by USERHOST to list replies to
46   #   the query list.  The reply string is composed as
47   #   follows:
48   #
49   # reply = nickname [ "*" ] "=" ( "+" / "-" ) hostname
50   #
51   # The '*' indicates whether the client has registered
52   # as an Operator.  The '-' or '+' characters represent
53   # whether the client has set an AWAY message or not
54   # respectively.
55   #
56   RPL_USERHOST=302
57
58   # ":*1<nick> *( " " <nick> )"
59   #
60   # - Reply format used by ISON to list replies to the
61   #   query list.
62   #
63   RPL_ISON=303
64
65   # - These replies are used with the AWAY command (if
66   #   allowed).  RPL_AWAY is sent to any client sending a
67   #   PRIVMSG to a client which is away.  RPL_AWAY is only
68   #   sent by the server to which the client is connected.
69   #   Replies RPL_UNAWAY and RPL_NOWAWAY are sent when the
70   #   client removes and sets an AWAY message.
71
72   # "<nick> :<away message>"
73   RPL_AWAY=301
74
75   # ":You are no longer marked as being away"
76   RPL_UNAWAY=305
77
78   # ":You have been marked as being away"
79   RPL_NOWAWAY=306
80
81   # - Replies 311 - 313, 317 - 319 are all replies
82   #   generated in response to a WHOIS message.  Given that
83   #   there are enough parameters present, the answering
84   #   server MUST either formulate a reply out of the above
85   #   numerics (if the query nick is found) or return an
86   #   error reply.  The '*' in RPL_WHOISUSER is there as
87   #   the literal character and not as a wild card.  For
88   #   each reply set, only RPL_WHOISCHANNELS may appear
89   #   more than once (for long lists of channel names).
90   #   The '@' and '+' characters next to the channel name
91   #   indicate whether a client is a channel operator or
92   #   has been granted permission to speak on a moderated
93   #   channel.  The RPL_ENDOFWHOIS reply is used to mark
94   #   the end of processing a WHOIS message.
95
96   # "<nick> <user> <host> * :<real name>"
97   RPL_WHOISUSER=311
98
99   # "<nick> <server> :<server info>"
100   RPL_WHOISSERVER=312
101
102   # "<nick> :is an IRC operator"
103   RPL_WHOISOPERATOR=313
104
105   # "<nick> <integer> :seconds idle"
106   RPL_WHOISIDLE=317
107
108   # "<nick> :End of WHOIS list"
109   RPL_ENDOFWHOIS=318
110
111   # "<nick> :*( ( "@" / "+" ) <channel> " " )"
112   RPL_WHOISCHANNELS=319
113
114   # - When replying to a WHOWAS message, a server MUST use
115   #   the replies RPL_WHOWASUSER, RPL_WHOISSERVER or
116   #   ERR_WASNOSUCHNICK for each nickname in the presented
117   #   list.  At the end of all reply batches, there MUST
118   #   be RPL_ENDOFWHOWAS (even if there was only one reply
119   #   and it was an error).
120
121   # "<nick> <user> <host> * :<real name>"
122   RPL_WHOWASUSER=314
123
124   # "<nick> :End of WHOWAS"
125   RPL_ENDOFWHOWAS=369
126
127   # - Replies RPL_LIST, RPL_LISTEND mark the actual replies
128   #   with data and end of the server's response to a LIST
129   #   command.  If there are no channels available to return,
130   #   only the end reply MUST be sent.
131
132   # Obsolete. Not used.
133   RPL_LISTSTART=321
134
135   # "<channel> <# visible> :<topic>"
136   RPL_LIST=322
137
138   # ":End of LIST"
139   RPL_LISTEND=323
140
141   # "<channel> <nickname>"
142   RPL_UNIQOPIS=325
143
144   # "<channel> <mode> <mode params>"
145   RPL_CHANNELMODEIS=324
146
147   # "<channel> <unixtime>"
148   RPL_CREATIONTIME=329
149
150   # "<channel> <url>"
151   RPL_CHANNEL_URL=328
152
153   # "<channel> :No topic is set"
154   RPL_NOTOPIC=331
155
156   # - When sending a TOPIC message to determine the
157   #   channel topic, one of two replies is sent.  If
158   #   the topic is set, RPL_TOPIC is sent back else
159   #   RPL_NOTOPIC.
160
161   # "<channel> :<topic>"
162   RPL_TOPIC=332
163
164   # <channel> <set by> <unixtime>
165   RPL_TOPIC_INFO=333
166
167   # "<channel> <nick>"
168   #
169   # - Returned by the server to indicate that the
170   #   attempted INVITE message was successful and is
171   #   being passed onto the end client.
172   #
173   RPL_INVITING=341
174
175   # "<user> :Summoning user to IRC"
176   #
177   # - Returned by a server answering a SUMMON message to
178   #   indicate that it is summoning that user.
179   #
180   RPL_SUMMONING=342
181
182   # "<channel> <invitemask>"
183   RPL_INVITELIST=346
184
185   # "<channel> :End of channel invite list"
186   #
187   # - When listing the 'invitations masks' for a given channel,
188   #   a server is required to send the list back using the
189   #   RPL_INVITELIST and RPL_ENDOFINVITELIST messages.  A
190   #   separate RPL_INVITELIST is sent for each active mask.
191   #   After the masks have been listed (or if none present) a
192   #   RPL_ENDOFINVITELIST MUST be sent.
193   #
194   RPL_ENDOFINVITELIST=347
195
196   # "<channel> <exceptionmask>"
197   RPL_EXCEPTLIST=348
198
199   # "<channel> :End of channel exception list"
200   #
201   # - When listing the 'exception masks' for a given channel,
202   #   a server is required to send the list back using the
203   #   RPL_EXCEPTLIST and RPL_ENDOFEXCEPTLIST messages.  A
204   #   separate RPL_EXCEPTLIST is sent for each active mask.
205   #   After the masks have been listed (or if none present)
206   #   a RPL_ENDOFEXCEPTLIST MUST be sent.
207   #
208   RPL_ENDOFEXCEPTLIST=349
209
210   # "<version>.<debuglevel> <server> :<comments>"
211   #
212   # - Reply by the server showing its version details.
213   #
214   # The <version> is the version of the software being
215   # used (including any patchlevel revisions) and the
216   # <debuglevel> is used to indicate if the server is
217   # running in "debug mode".
218   #
219   # The "comments" field may contain any comments about
220   # the version or further version details.
221   #
222   RPL_VERSION=351
223
224   # - The RPL_WHOREPLY and RPL_ENDOFWHO pair are used
225   #   to answer a WHO message.  The RPL_WHOREPLY is only
226   #   sent if there is an appropriate match to the WHO
227   #   query.  If there is a list of parameters supplied
228   #   with a WHO message, a RPL_ENDOFWHO MUST be sent
229   #   after processing each list item with <name> being
230   #   the item.
231
232   # "<channel> <user> <host> <server> <nick>
233   # ( "H" / "G" > ["*"] [ ( "@" / "+" ) ]
234   # :<hopcount> <real name>"
235   #
236   RPL_WHOREPLY=352
237
238   # "<name> :End of WHO list"
239   RPL_ENDOFWHO=315
240
241   # - To reply to a NAMES message, a reply pair consisting
242   #   of RPL_NAMREPLY and RPL_ENDOFNAMES is sent by the
243   #   server back to the client.  If there is no channel
244   #   found as in the query, then only RPL_ENDOFNAMES is
245   #   returned.  The exception to this is when a NAMES
246   #   message is sent with no parameters and all visible
247   #   channels and contents are sent back in a series of
248   #   RPL_NAMEREPLY messages with a RPL_ENDOFNAMES to mark
249   #   the end.
250
251   # "( "=" / "*" / "@" ) <channel>
252   # :[ "@" / "+" ] <nick> *( " " [ "@" / "+" ] <nick> )
253   # - "@" is used for secret channels, "*" for private
254   # channels, and "=" for others (public channels).
255   #
256   RPL_NAMREPLY=353
257
258   # "<channel> :End of NAMES list"
259   RPL_ENDOFNAMES=366
260
261   # - In replying to the LINKS message, a server MUST send
262   #   replies back using the RPL_LINKS numeric and mark the
263   #   end of the list using an RPL_ENDOFLINKS reply.
264
265   # "<mask> <server> :<hopcount> <server info>"
266   RPL_LINKS=364
267
268   # "<mask> :End of LINKS list"
269   RPL_ENDOFLINKS=365
270
271   # - When listing the active 'bans' for a given channel,
272   #   a server is required to send the list back using the
273   #   RPL_BANLIST and RPL_ENDOFBANLIST messages.  A separate
274   #   RPL_BANLIST is sent for each active banmask.  After the
275   #   banmasks have been listed (or if none present) a
276   #   RPL_ENDOFBANLIST MUST be sent.
277
278   # "<channel> <banmask>"
279   RPL_BANLIST=367
280
281   # "<channel> :End of channel ban list"
282   RPL_ENDOFBANLIST=368
283
284   # - A server responding to an INFO message is required to
285   #   send all its 'info' in a series of RPL_INFO messages
286   #   with a RPL_ENDOFINFO reply to indicate the end of the
287   #   replies.
288
289   # ":<string>"
290   RPL_INFO=371
291
292   # ":End of INFO list"
293   RPL_ENDOFINFO=374
294
295   # - When responding to the MOTD message and the MOTD file
296   # is found, the file is displayed line by line, with
297   # each line no longer than 80 characters, using
298   # RPL_MOTD format replies.  These MUST be surrounded
299   # by a RPL_MOTDSTART (before the RPL_MOTDs) and an
300   # RPL_ENDOFMOTD (after).
301
302   # ":- <server> Message of the day - "
303   RPL_MOTDSTART=375
304
305   # ":- <text>"
306   RPL_MOTD=372
307
308   # ":End of MOTD command"
309   RPL_ENDOFMOTD=376
310
311   # ":You are now an IRC operator"
312   #
313   # - RPL_YOUREOPER is sent back to a client which has
314   #   just successfully issued an OPER message and gained
315   #   operator status.
316   #
317   RPL_YOUREOPER=381
318
319   # "<config file> :Rehashing"
320   #
321   # - If the REHASH option is used and an operator sends
322   #   a REHASH message, an RPL_REHASHING is sent back to
323   #   the operator.
324   #
325   RPL_REHASHING=382
326
327   # "You are service <servicename>"
328   #
329   # - Sent by the server to a service upon successful
330   #   registration.
331   #
332   RPL_YOURESERVICE=383
333
334   # "<server> :<string showing server's local time>"
335   #
336   # - When replying to the TIME message, a server MUST send
337   #   the reply using the RPL_TIME format above.  The string
338   #   showing the time need only contain the correct day and
339   #   time there.  There is no further requirement for the
340   #   time string.
341   #
342   RPL_TIME=391
343
344   # - If the USERS message is handled by a server, the
345   #   replies RPL_USERSTART, RPL_USERS, RPL_ENDOFUSERS and
346   #   RPL_NOUSERS are used.  RPL_USERSSTART MUST be sent
347   #   first, following by either a sequence of RPL_USERS
348   #   or a single RPL_NOUSER.  Following this is
349   #   RPL_ENDOFUSERS.
350
351   # ":UserID   Terminal  Host"
352   RPL_USERSSTART=392
353
354   # ":<username> <ttyline> <hostname>"
355   RPL_USERS=393
356
357   # ":End of users"
358   RPL_ENDOFUSERS=394
359
360   # ":Nobody logged in"
361   RPL_NOUSERS=395
362
363   # - The RPL_TRACE* are all returned by the server in
364   #   response to the TRACE message.  How many are
365   #   returned is dependent on the TRACE message and
366   #   whether it was sent by an operator or not.  There
367   #   is no predefined order for which occurs first.
368   #   Replies RPL_TRACEUNKNOWN, RPL_TRACECONNECTING and
369   #   RPL_TRACEHANDSHAKE are all used for connections
370   #   which have not been fully established and are either
371   #   unknown, still attempting to connect or in the
372   #   process of completing the 'server handshake'.
373   #   RPL_TRACELINK is sent by any server which handles
374   #   a TRACE message and has to pass it on to another
375   #   server.  The list of RPL_TRACELINKs sent in
376   #   response to a TRACE command traversing the IRC
377   #   network should reflect the actual connectivity of
378   #   the servers themselves along that path.
379   #
380   # RPL_TRACENEWTYPE is to be used for any connection
381   # which does not fit in the other categories but is
382   # being displayed anyway.
383   # RPL_TRACEEND is sent to indicate the end of the list.
384
385   # "Link <version & debug level> <destination>
386   # <next server> V<protocol version>
387   # <link uptime in seconds> <backstream sendq>
388   # <upstream sendq>"
389   RPL_TRACELINK=200
390
391   # "Try. <class> <server>"
392   RPL_TRACECONNECTING=201
393
394   # "H.S. <class> <server>"
395   RPL_TRACEHANDSHAKE=202
396
397   # "???? <class> [<client IP address in dot form>]"
398   RPL_TRACEUNKNOWN=203
399
400   # "Oper <class> <nick>"
401   RPL_TRACEOPERATOR=204
402
403   # "User <class> <nick>"
404   RPL_TRACEUSER=205
405
406   # "Serv <class> <int>S <int>C <server>
407   # <nick!user|*!*>@<host|server> V<protocol version>"
408   RPL_TRACESERVER=206
409
410   # "Service <class> <name> <type> <active type>"
411   RPL_TRACESERVICE=207
412
413   # "<newtype> 0 <client name>"
414   RPL_TRACENEWTYPE=208
415
416   # "Class <class> <count>"
417   RPL_TRACECLASS=209
418
419   # Unused.
420   RPL_TRACERECONNECT=210
421
422   # "File <logfile> <debug level>"
423   RPL_TRACELOG=261
424
425   # "<server name> <version & debug level> :End of TRACE"
426   RPL_TRACEEND=262
427
428   # ":Current local  users: 3  Max: 4"
429   RPL_LOCALUSERS=265
430
431   # ":Current global users: 3  Max: 4"
432   RPL_GLOBALUSERS=266
433
434   # "::Highest connection count: 4 (4 clients) (251 since server was
435   # (re)started)"
436   RPL_STATSCONN=250
437
438   # "<linkname> <sendq> <sent messages>
439   # <sent Kbytes> <received messages>
440   # <received Kbytes> <time open>"
441   #
442   # - reports statistics on a connection.  <linkname>
443   #   identifies the particular connection, <sendq> is
444   #   the amount of data that is queued and waiting to be
445   #   sent <sent messages> the number of messages sent,
446   #   and <sent Kbytes> the amount of data sent, in
447   #   Kbytes. <received messages> and <received Kbytes>
448   #   are the equivalent of <sent messages> and <sent
449   #   Kbytes> for received data, respectively.  <time
450   #   open> indicates how long ago the connection was
451   #   opened, in seconds.
452   #
453   RPL_STATSLINKINFO=211
454
455   # "<command> <count> <byte count> <remote count>"
456   #
457   # - reports statistics on commands usage.
458   #
459   RPL_STATSCOMMANDS=212
460
461   # "<stats letter> :End of STATS report"
462   #
463   RPL_ENDOFSTATS=219
464
465   # ":Server Up %d days %d:%02d:%02d"
466   #
467   # - reports the server uptime.
468   #
469   RPL_STATSUPTIME=242
470
471   # "O <hostmask> * <name>"
472   #
473   # - reports the allowed hosts from where user may become IRC
474   #   operators.
475   #
476   RPL_STATSOLINE=243
477
478   # "<user mode string>"
479   #
480   # - To answer a query about a client's own mode,
481   #   RPL_UMODEIS is sent back.
482   #
483   RPL_UMODEIS=221
484
485   # - When listing services in reply to a SERVLIST message,
486   #   a server is required to send the list back using the
487   #   RPL_SERVLIST and RPL_SERVLISTEND messages.  A separate
488   #   RPL_SERVLIST is sent for each service.  After the
489   #   services have been listed (or if none present) a
490   #   RPL_SERVLISTEND MUST be sent.
491
492   # "<name> <server> <mask> <type> <hopcount> <info>"
493   RPL_SERVLIST=234
494
495   # "<mask> <type> :End of service listing"
496   RPL_SERVLISTEND=235
497
498   # - In processing an LUSERS message, the server
499   #   sends a set of replies from RPL_LUSERCLIENT,
500   #   RPL_LUSEROP, RPL_USERUNKNOWN,
501   #   RPL_LUSERCHANNELS and RPL_LUSERME.  When
502   #   replying, a server MUST send back
503   #   RPL_LUSERCLIENT and RPL_LUSERME.  The other
504   #   replies are only sent back if a non-zero count
505   #   is found for them.
506
507   # ":There are <integer> users and <integer>
508   # services on <integer> servers"
509   RPL_LUSERCLIENT=251
510
511   # "<integer> :operator(s) online"
512   RPL_LUSEROP=252
513
514   # "<integer> :unknown connection(s)"
515   RPL_LUSERUNKNOWN=253
516
517   # "<integer> :channels formed"
518   RPL_LUSERCHANNELS=254
519
520   # ":I have <integer> clients and <integer> servers"
521   RPL_LUSERME=255
522
523   # - When replying to an ADMIN message, a server
524   # is expected to use replies RPL_ADMINME
525   # through to RPL_ADMINEMAIL and provide a text
526   # message with each.  For RPL_ADMINLOC1 a
527   # description of what city, state and country
528   # the server is in is expected, followed by
529   # details of the institution (RPL_ADMINLOC2)
530   # and finally the administrative contact for the
531   # server (an email address here is REQUIRED)
532   # in RPL_ADMINEMAIL.
533
534   # "<server> :Administrative info"
535   RPL_ADMINME=256
536
537   # ":<admin info>"
538   RPL_ADMINLOC1=257
539
540   # ":<admin info>"
541   RPL_ADMINLOC2=258
542
543   # ":<admin info>"
544   RPL_ADMINEMAIL=259
545
546   # "<command> :Please wait a while and try again."
547   #
548   # - When a server drops a command without processing it,
549   #   it MUST use the reply RPL_TRYAGAIN to inform the
550   #   originating client.
551   RPL_TRYAGAIN=263
552
553   # 5.2 Error Replies
554   #
555   # Error replies are found in the range from 400 to 599.
556
557   # "<nickname> :No such nick/channel"
558   #
559   # - Used to indicate the nickname parameter supplied to a
560   #   command is currently unused.
561   #
562   ERR_NOSUCHNICK=401
563
564   # "<server name> :No such server"
565   #
566   # - Used to indicate the server name given currently
567   #   does not exist.
568   #
569   ERR_NOSUCHSERVER=402
570
571   # "<channel name> :No such channel"
572   #
573   # - Used to indicate the given channel name is invalid.
574   #
575   ERR_NOSUCHCHANNEL=403
576
577   # "<channel name> :Cannot send to channel"
578   #
579   # - Sent to a user who is either (a) not on a channel
580   #   which is mode +n or (b) not a chanop (or mode +v) on
581   #   a channel which has mode +m set or where the user is
582   #   banned and is trying to send a PRIVMSG message to
583   #   that channel.
584   #
585   ERR_CANNOTSENDTOCHAN=404
586
587   # "<channel name> :You have joined too many channels"
588   #
589   # - Sent to a user when they have joined the maximum
590   #   number of allowed channels and they try to join
591   #   another channel.
592   #
593   ERR_TOOMANYCHANNELS=405
594
595   # "<nickname> :There was no such nickname"
596   #
597   # - Returned by WHOWAS to indicate there is no history
598   #   information for that nickname.
599   #
600   ERR_WASNOSUCHNICK=406
601
602   # "<target> :<error code> recipients. <abort message>"
603   #
604   # - Returned to a client which is attempting to send a
605   #   PRIVMSG/NOTICE using the user@host destination format
606   #   and for a user@host which has several occurrences.
607   #
608   # - Returned to a client which trying to send a
609   #   PRIVMSG/NOTICE to too many recipients.
610   #
611   # - Returned to a client which is attempting to JOIN a safe
612   #   channel using the shortname when there are more than one
613   #   such channel.
614   #
615   ERR_TOOMANYTARGETS=407
616
617   # "<service name> :No such service"
618   #
619   # - Returned to a client which is attempting to send a SQUERY
620   #   to a service which does not exist.
621   #
622   ERR_NOSUCHSERVICE=408
623
624   # ":No origin specified"
625   #
626   # - PING or PONG message missing the originator parameter.
627   #
628   ERR_NOORIGIN=409
629
630   # ":No recipient given (<command>)"
631   ERR_NORECIPIENT=411
632
633   # - 412 - 415 are returned by PRIVMSG to indicate that
634   #   the message wasn't delivered for some reason.
635   #   ERR_NOTOPLEVEL and ERR_WILDTOPLEVEL are errors that
636   #   are returned when an invalid use of
637   #   "PRIVMSG $<server>" or "PRIVMSG #<host>" is attempted.
638
639   # ":No text to send"
640   ERR_NOTEXTTOSEND=412
641
642   # "<mask> :No toplevel domain specified"
643   ERR_NOTOPLEVEL=413
644
645   # "<mask> :Wildcard in toplevel domain"
646   ERR_WILDTOPLEVEL=414
647
648   # "<mask> :Bad Server/host mask"
649   ERR_BADMASK=415
650
651   # "<command> :Unknown command"
652   #
653   # - Returned to a registered client to indicate that the
654   #   command sent is unknown by the server.
655   #
656   ERR_UNKNOWNCOMMAND=421
657
658   # ":MOTD File is missing"
659   #
660   # - Server's MOTD file could not be opened by the server.
661   #
662   ERR_NOMOTD=422
663
664   # "<server> :No administrative info available"
665   #
666   # - Returned by a server in response to an ADMIN message
667   #   when there is an error in finding the appropriate
668   #   information.
669   #
670   ERR_NOADMININFO=423
671
672   # ":File error doing <file op> on <file>"
673   #
674   # - Generic error message used to report a failed file
675   #   operation during the processing of a message.
676   #
677   ERR_FILEERROR=424
678
679   # ":No nickname given"
680   #
681   # - Returned when a nickname parameter expected for a
682   #   command and isn't found.
683   #
684   ERR_NONICKNAMEGIVEN=431
685
686   # "<nick> :Erroneous nickname"
687   #
688   # - Returned after receiving a NICK message which contains
689   #   characters which do not fall in the defined set.  See
690   #   section 2.3.1 for details on valid nicknames.
691   #
692   ERR_ERRONEUSNICKNAME=432
693
694   # "<nick> :Nickname is already in use"
695   #
696   # - Returned when a NICK message is processed that results
697   #   in an attempt to change to a currently existing
698   #   nickname.
699   #
700   ERR_NICKNAMEINUSE=433
701
702   # "<nick> :Nickname collision KILL from <user>@<host>"
703   #
704   # - Returned by a server to a client when it detects a
705   #   nickname collision (registered of a NICK that
706   #   already exists by another server).
707   #
708   ERR_NICKCOLLISION=436
709
710   # "<nick/channel> :Nick/channel is temporarily unavailable"
711   #
712   # - Returned by a server to a user trying to join a channel
713   #   currently blocked by the channel delay mechanism.
714   #
715   # - Returned by a server to a user trying to change nickname
716   #   when the desired nickname is blocked by the nick delay
717   #   mechanism.
718   #
719   ERR_UNAVAILRESOURCE=437
720
721   # "<nick> <channel> :They aren't on that channel"
722   #
723   # - Returned by the server to indicate that the target
724   #   user of the command is not on the given channel.
725   #
726   ERR_USERNOTINCHANNEL=441
727
728   # "<channel> :You're not on that channel"
729   #
730   # - Returned by the server whenever a client tries to
731   #   perform a channel affecting command for which the
732   #   client isn't a member.
733   #
734   ERR_NOTONCHANNEL=442
735
736   # "<user> <channel> :is already on channel"
737   #
738   # - Returned when a client tries to invite a user to a
739   #   channel they are already on.
740   #
741   ERR_USERONCHANNEL=443
742
743   # "<user> :User not logged in"
744   #
745   # - Returned by the summon after a SUMMON command for a
746   #   user was unable to be performed since they were not
747   #   logged in.
748   #
749   ERR_NOLOGIN=444
750
751   # ":SUMMON has been disabled"
752   #
753   # - Returned as a response to the SUMMON command.  MUST be
754   #   returned by any server which doesn't implement it.
755   #
756   ERR_SUMMONDISABLED=445
757
758   # ":USERS has been disabled"
759   #
760   # - Returned as a response to the USERS command.  MUST be
761   #   returned by any server which does not implement it.
762   #
763   ERR_USERSDISABLED=446
764
765   # ":You have not registered"
766   #
767   # - Returned by the server to indicate that the client
768   #   MUST be registered before the server will allow it
769   #   to be parsed in detail.
770   #
771   ERR_NOTREGISTERED=451
772
773   # "<command> :Not enough parameters"
774   #
775   # - Returned by the server by numerous commands to
776   #   indicate to the client that it didn't supply enough
777   #   parameters.
778   #
779   ERR_NEEDMOREPARAMS=461
780
781   # ":Unauthorized command (already registered)"
782   #
783   # - Returned by the server to any link which tries to
784   #   change part of the registered details (such as
785   #   password or user details from second USER message).
786   #
787   ERR_ALREADYREGISTRED=462
788
789   # ":Your host isn't among the privileged"
790   #
791   # - Returned to a client which attempts to register with
792   #   a server which does not been setup to allow
793   #   connections from the host the attempted connection
794   #   is tried.
795   #
796   ERR_NOPERMFORHOST=463
797
798   # ":Password incorrect"
799   #
800   # - Returned to indicate a failed attempt at registering
801   #   a connection for which a password was required and
802   #   was either not given or incorrect.
803   #
804   ERR_PASSWDMISMATCH=464
805
806   # ":You are banned from this server"
807   #
808   # - Returned after an attempt to connect and register
809   #   yourself with a server which has been setup to
810   #   explicitly deny connections to you.
811   #
812   ERR_YOUREBANNEDCREEP=465
813
814   # - Sent by a server to a user to inform that access to the
815   #   server will soon be denied.
816   #
817   ERR_YOUWILLBEBANNED=466
818
819   # "<channel> :Channel key already set"
820   ERR_KEYSET=467
821
822   # "<channel> :Cannot join channel (+l)"
823   ERR_CHANNELISFULL=471
824
825   # "<char> :is unknown mode char to me for <channel>"
826   ERR_UNKNOWNMODE=472
827
828   # "<channel> :Cannot join channel (+i)"
829   ERR_INVITEONLYCHAN=473
830
831   # "<channel> :Cannot join channel (+b)"
832   ERR_BANNEDFROMCHAN=474
833
834   # "<channel> :Cannot join channel (+k)"
835   ERR_BADCHANNELKEY=475
836
837   # "<channel> :Bad Channel Mask"
838   ERR_BADCHANMASK=476
839
840   # "<channel> :Channel doesn't support modes"
841   ERR_NOCHANMODES=477
842
843   # "<channel> <char> :Channel list is full"
844   #
845   ERR_BANLISTFULL=478
846
847   # ":Permission Denied- You're not an IRC operator"
848   #
849   # - Any command requiring operator privileges to operate
850   #   MUST return this error to indicate the attempt was
851   #   unsuccessful.
852   #
853   ERR_NOPRIVILEGES=481
854
855   # "<channel> :You're not channel operator"
856   #
857   # - Any command requiring 'chanop' privileges (such as
858   #   MODE messages) MUST return this error if the client
859   #   making the attempt is not a chanop on the specified
860   #   channel.
861   #
862   #
863   ERR_CHANOPRIVSNEEDED=482
864
865   # ":You can't kill a server!"
866   #
867   # - Any attempts to use the KILL command on a server
868   #   are to be refused and this error returned directly
869   #   to the client.
870   #
871   ERR_CANTKILLSERVER=483
872
873   # ":Your connection is restricted!"
874   #
875   # - Sent by the server to a user upon connection to indicate
876   #   the restricted nature of the connection (user mode "+r").
877   #
878   ERR_RESTRICTED=484
879
880   # ":You're not the original channel operator"
881   #
882   # - Any MODE requiring "channel creator" privileges MUST
883   #   return this error if the client making the attempt is not
884   #   a chanop on the specified channel.
885   #
886   ERR_UNIQOPPRIVSNEEDED=485
887
888   # ":No O-lines for your host"
889   #
890   # - If a client sends an OPER message and the server has
891   #   not been configured to allow connections from the
892   #   client's host as an operator, this error MUST be
893   #   returned.
894   #
895   ERR_NOOPERHOST=491
896
897   # ":Unknown MODE flag"
898   #
899   # - Returned by the server to indicate that a MODE
900   #   message was sent with a nickname parameter and that
901   #   the a mode flag sent was not recognized.
902   #
903   ERR_UMODEUNKNOWNFLAG=501
904
905   # ":Cannot change mode for other users"
906   #
907   # - Error sent to any user trying to view or change the
908   #   user mode for a user other than themselves.
909   #
910   ERR_USERSDONTMATCH=502
911
912   # 5.3 Reserved numerics
913   #
914   # These numerics are not described above since they fall into one of
915   # the following categories:
916   #
917   # 1. no longer in use;
918   #
919   # 2. reserved for future planned use;
920   #
921   # 3. in current use but are part of a non-generic 'feature' of
922   #    the current IRC server.
923   #
924   RPL_SERVICEINFO=231
925   RPL_ENDOFSERVICES=232
926   RPL_SERVICE=233
927   RPL_NONE=300
928   RPL_WHOISCHANOP=316
929   RPL_KILLDONE=361
930   RPL_CLOSING=362
931   RPL_CLOSEEND=363
932   RPL_INFOSTART=373
933   RPL_MYPORTIS=384
934   RPL_STATSCLINE=213
935   RPL_STATSNLINE=214
936   RPL_STATSILINE=215
937   RPL_STATSKLINE=216
938   RPL_STATSQLINE=217
939   RPL_STATSYLINE=218
940   RPL_STATSVLINE=240
941   RPL_STATSLLINE=241
942   RPL_STATSHLINE=244
943   RPL_STATSSLINE=244
944   RPL_STATSPING=246
945   RPL_STATSBLINE=247
946   ERR_NOSERVICEHOST=492
947   RPL_DATASTR=290
948
949   # A structure to hold LIST data, in the Irc namespace
950   ListData = Struct.new :channel, :users, :topic
951
952   # Implements RFC 2812 and prior IRC RFCs.
953   #
954   # Clients should register Proc{}s to handle the various server events, and
955   # the Client class will handle dispatch.
956   class Client
957
958     # the Server we're connected to
959     attr_reader :server
960     # the User representing us on that server
961     attr_reader :user
962
963     # Create a new Client instance
964     def initialize
965       @server = Server.new         # The Server
966       @user = @server.user("*!*@*")     # The User representing the client on this Server
967
968       @handlers = Hash.new
969
970       # This is used by some messages to build lists of users that
971       # will be delegated when the ENDOF... message is received
972       @tmpusers = []
973
974       # Same as above, just for bans
975       @tmpbans = []
976     end
977
978     # Clear the server and reset the user
979     def reset
980       @server.clear
981       @user = @server.user("*!*@*")
982     end
983
984     # key::   server event to handle
985     # value:: proc object called when event occurs
986     # set a handler for a server event
987     #
988     # ==server events currently supported:
989     #
990     # TODO handle errors ERR_CHANOPRIVSNEEDED, ERR_CANNOTSENDTOCHAN
991     #
992     # welcome::     server welcome message on connect
993     # yourhost::    your host details (on connection)
994     # created::     when the server was started
995     # isupport::    information about what this server supports
996     # ping::        server pings you (default handler returns a pong)
997     # nicktaken::   you tried to change nick to one that's in use
998     # badnick::     you tried to change nick to one that's invalid
999     # topic::       someone changed the topic of a channel
1000     # topicinfo::   on joining a channel or asking for the topic, tells you
1001     #               who set it and when
1002     # names::       server sends list of channel members when you join
1003     # motd::        server message of the day
1004     # privmsg::     privmsg, the core of IRC, a message to you from someone
1005     # public::      optionally instead of getting privmsg you can hook to only
1006     #               the public ones...
1007     # msg::         or only the private ones, or both
1008     # kick::        someone got kicked from a channel
1009     # part::        someone left a channel
1010     # quit::        someone quit IRC
1011     # join::        someone joined a channel
1012     # changetopic:: the topic of a channel changed
1013     # invite::      you are invited to a channel
1014     # nick::        someone changed their nick
1015     # mode::        a mode change
1016     # notice::      someone sends you a notice
1017     # unknown::     any other message not handled by the above
1018     def []=(key, value)
1019       @handlers[key] = value
1020     end
1021
1022     # key:: event name
1023     # remove a handler for a server event
1024     def deletehandler(key)
1025       @handlers.delete(key)
1026     end
1027
1028     # takes a server string, checks for PING, PRIVMSG, NOTIFY, etc, and parses
1029     # numeric server replies, calling the appropriate handler for each, and
1030     # sending it a hash containing the data from the server
1031     def process(serverstring)
1032       data = Hash.new
1033       data[:serverstring] = serverstring
1034
1035       unless serverstring.chomp =~ /^(:(\S+)\s)?(\S+)(\s(.*))?$/
1036         raise "Unparseable Server Message!!!: #{serverstring.inspect}"
1037       end
1038
1039       prefix, command, params = $2, $3, $5
1040
1041       if prefix != nil
1042         # Most servers will send a full nick!user@host prefix for
1043         # messages from users. Therefore, when the prefix doesn't match this
1044         # syntax it's usually the server hostname.
1045         #
1046         # This is not always true, though, since some servers do not send a
1047         # full hostmask for user messages.
1048         #
1049         if prefix =~ /^#{Regexp::Irc::BANG_AT}$/
1050           data[:source] = @server.user(prefix)
1051         else
1052           if @server.hostname
1053             if @server.hostname != prefix
1054               # TODO do we want to be able to differentiate messages that are passed on to us from /other/ servers?
1055               debug "Origin #{prefix} for message\n\t#{serverstring.inspect}\nis neither a user hostmask nor the server hostname\nI'll pretend that it's from the server anyway"
1056               data[:source] = @server
1057             else
1058               data[:source] = @server
1059             end
1060           else
1061             @server.instance_variable_set(:@hostname, prefix)
1062             data[:source] = @server
1063           end
1064         end
1065       end
1066
1067       # split parameters in an array
1068       argv = []
1069       params.scan(/(?!:)(\S+)|:(.*)/) { argv << ($1 || $2) } if params
1070
1071       if command =~ /^(\d+)$/ # Numeric replies
1072         data[:target] = argv[0]
1073         # A numeric reply /should/ be directed at the client, except when we're connecting with a used nick, in which case
1074         # it's directed at '*'
1075         not_us = !([@user.nick, '*'].include?(data[:target]))
1076         if not_us
1077           warning "Server reply #{serverstring.inspect} directed at #{data[:target]} instead of client (#{@user.nick})"
1078         end
1079
1080         num=command.to_i
1081         case num
1082         when RPL_WELCOME
1083           data[:message] = argv[1]
1084           # "Welcome to the Internet Relay Network
1085           # <nick>!<user>@<host>"
1086           if not_us
1087             warning "Server thinks client (#{@user.inspect}) has a different nick"
1088             @user.nick = data[:target]
1089           end
1090           if data[:message] =~ /([^@!\s]+)(?:!([^@!\s]+?))?@(\S+)/
1091             nick = $1
1092             user = $2
1093             host = $3
1094             warning "Welcome message nick mismatch (#{nick} vs #{data[:target]})" if nick != data[:target]
1095             @user.user = user if user
1096             @user.host = host if host
1097           end
1098           handle(:welcome, data)
1099         when RPL_YOURHOST
1100           # "Your host is <servername>, running version <ver>"
1101           data[:message] = argv[1]
1102           handle(:yourhost, data)
1103         when RPL_CREATED
1104           # "This server was created <date>"
1105           data[:message] = argv[1]
1106           handle(:created, data)
1107         when RPL_MYINFO
1108           # "<servername> <version> <available user modes>
1109           # <available channel modes>"
1110           @server.parse_my_info(params.split(' ', 2).last)
1111           data[:servername] = @server.hostname
1112           data[:version] = @server.version
1113           data[:usermodes] = @server.usermodes
1114           data[:chanmodes] = @server.chanmodes
1115           handle(:myinfo, data)
1116         when RPL_ISUPPORT
1117           # "PREFIX=(ov)@+ CHANTYPES=#& :are supported by this server"
1118           # "MODES=4 CHANLIMIT=#:20 NICKLEN=16 USERLEN=10 HOSTLEN=63
1119           # TOPICLEN=450 KICKLEN=450 CHANNELLEN=30 KEYLEN=23 CHANTYPES=#
1120           # PREFIX=(ov)@+ CASEMAPPING=ascii CAPAB IRCD=dancer :are available
1121           # on this server"
1122           #
1123           @server.parse_isupport(argv[1..-2].join(' '))
1124           handle(:isupport, data)
1125         when ERR_NICKNAMEINUSE
1126           # "* <nick> :Nickname is already in use"
1127           data[:nick] = argv[1]
1128           data[:message] = argv[2]
1129           handle(:nicktaken, data)
1130         when ERR_ERRONEUSNICKNAME
1131           # "* <nick> :Erroneous nickname"
1132           data[:nick] = argv[1]
1133           data[:message] = argv[2]
1134           handle(:badnick, data)
1135         when RPL_TOPIC
1136           data[:channel] = @server.channel(argv[1])
1137           data[:topic] = argv[2]
1138           data[:channel].topic.text = data[:topic]
1139
1140           handle(:topic, data)
1141         when RPL_TOPIC_INFO
1142           data[:nick] = @server.user(argv[0])
1143           data[:channel] = @server.channel(argv[1])
1144
1145           # This must not be an IRC::User because it might not be an actual User,
1146           # and we risk overwriting valid User data
1147           data[:source] = argv[2].to_irc_netmask(:server => @server)
1148
1149           data[:time] = Time.at(argv[3].to_i)
1150
1151           data[:channel].topic.set_by = data[:source]
1152           data[:channel].topic.set_on = data[:time]
1153
1154           handle(:topicinfo, data)
1155         when RPL_NAMREPLY
1156           # "( "=" / "*" / "@" ) <channel>
1157           # :[ "@" / "+" ] <nick> *( " " [ "@" / "+" ] <nick> )
1158           # - "@" is used for secret channels, "*" for private
1159           # channels, and "=" for others (public channels).
1160           data[:channeltype] = argv[1]
1161           data[:channel] = chan = @server.channel(argv[2])
1162
1163           users = []
1164           argv[3].scan(/\S+/).each { |u|
1165             # FIXME beware of servers that allow multiple prefixes
1166             if(u =~ /^([#{@server.supports[:prefix][:prefixes].join}])?(.*)$/)
1167               umode = $1
1168               user = $2
1169               users << [user, umode]
1170             end
1171           }
1172
1173           users.each { |ar|
1174             u = @server.user(ar[0])
1175             chan.add_user(u, :silent => true)
1176             debug "Adding user #{u}"
1177             if ar[1]
1178               ms = @server.mode_for_prefix(ar[1].to_sym)
1179               debug "\twith mode #{ar[1]} (#{ms})"
1180               chan.mode[ms].set(u)
1181             end
1182           }
1183           @tmpusers += users
1184         when RPL_ENDOFNAMES
1185           data[:channel] = @server.channel(argv[1])
1186           data[:users] = @tmpusers
1187           handle(:names, data)
1188           @tmpusers = Array.new
1189         when RPL_BANLIST
1190           data[:channel] = @server.channel(argv[1])
1191           data[:mask] = argv[2]
1192           data[:by] = argv[3]
1193           data[:at] = argv[4]
1194           @tmpbans << data
1195         when RPL_ENDOFBANLIST
1196           data[:channel] = @server.channel(argv[1])
1197           data[:bans] = @tmpbans
1198           handle(:banlist, data)
1199           @tmpbans = Array.new
1200         when RPL_LUSERCLIENT
1201           # ":There are <integer> users and <integer>
1202           # services on <integer> servers"
1203           data[:message] = argv[1]
1204           handle(:luserclient, data)
1205         when RPL_LUSEROP
1206           # "<integer> :operator(s) online"
1207           data[:ops] = argv[1].to_i
1208           handle(:luserop, data)
1209         when RPL_LUSERUNKNOWN
1210           # "<integer> :unknown connection(s)"
1211           data[:unknown] = argv[1].to_i
1212           handle(:luserunknown, data)
1213         when RPL_LUSERCHANNELS
1214           # "<integer> :channels formed"
1215           data[:channels] = argv[1].to_i
1216           handle(:luserchannels, data)
1217         when RPL_LUSERME
1218           # ":I have <integer> clients and <integer> servers"
1219           data[:message] = argv[1]
1220           handle(:luserme, data)
1221         when ERR_NOMOTD
1222           # ":MOTD File is missing"
1223           data[:message] = argv[1]
1224           handle(:motd_missing, data)
1225         when RPL_LOCALUSERS
1226           # ":Current local  users: 3  Max: 4"
1227           data[:message] = argv[1]
1228           handle(:localusers, data)
1229         when RPL_GLOBALUSERS
1230           # ":Current global users: 3  Max: 4"
1231           data[:message] = argv[1]
1232           handle(:globalusers, data)
1233         when RPL_STATSCONN
1234           # ":Highest connection count: 4 (4 clients) (251 since server was
1235           # (re)started)"
1236           data[:message] = argv[1]
1237           handle(:statsconn, data)
1238         when RPL_MOTDSTART
1239           # "<nick> :- <server> Message of the Day -"
1240           if argv[1] =~ /^-\s+(\S+)\s/
1241             server = $1
1242           else
1243             warning "Server doesn't have an RFC compliant MOTD start."
1244           end
1245           @motd = ""
1246         when RPL_MOTD
1247           if(argv[1] =~ /^-\s+(.*)$/)
1248             @motd << $1
1249             @motd << "\n"
1250           end
1251         when RPL_ENDOFMOTD
1252           data[:motd] = @motd
1253           handle(:motd, data)
1254         when RPL_DATASTR
1255           data[:text] = argv[1]
1256           handle(:datastr, data)
1257         when RPL_AWAY
1258           data[:nick] = user = @server.user(argv[1])
1259           data[:message] = argv[-1]
1260           user.away = data[:message]
1261           handle(:away, data)
1262         when RPL_WHOREPLY
1263           data[:channel] = channel = @server.channel(argv[1])
1264           data[:user] = argv[2]
1265           data[:host] = argv[3]
1266           data[:userserver] = argv[4]
1267           data[:nick] = user = @server.user(argv[5])
1268           if argv[6] =~ /^(H|G)(\*)?(.*)?$/
1269             data[:away] = ($1 == 'G')
1270             data[:ircop] = $2
1271             data[:modes] = $3.scan(/./).map { |mode|
1272               m = @server.supports[:prefix][:prefixes].index(mode.to_sym)
1273               @server.supports[:prefix][:modes][m]
1274             } rescue []
1275           else
1276             warning "Strange WHO reply: #{serverstring.inspect}"
1277           end
1278           data[:hopcount], data[:real_name] = argv[7].split(" ", 2)
1279
1280           user.user = data[:user]
1281           user.host = data[:host]
1282           user.away = data[:away] # FIXME doesn't provide the actual message
1283           # TODO ircop status
1284           # TODO userserver
1285           # TODO hopcount
1286           user.real_name = data[:real_name]
1287
1288           channel.add_user(user, :silent=>true)
1289           data[:modes].map { |mode|
1290             channel.mode[mode].set(user)
1291           }
1292
1293           handle(:who, data)
1294         when RPL_ENDOFWHO
1295           handle(:eowho, data)
1296         when RPL_WHOISUSER
1297           @whois ||= Hash.new
1298           @whois[:nick] = argv[1]
1299           @whois[:user] = argv[2]
1300           @whois[:host] = argv[3]
1301           @whois[:real_name] = argv[-1]
1302
1303           user = @server.user(@whois[:nick])
1304           user.user = @whois[:user]
1305           user.host = @whois[:host]
1306           user.real_name = @whois[:real_name]
1307         when RPL_WHOISSERVER
1308           @whois ||= Hash.new
1309           @whois[:nick] = argv[1]
1310           @whois[:server] = argv[2]
1311           @whois[:server_info] = argv[-1]
1312           # TODO update user info
1313         when RPL_WHOISOPERATOR
1314           @whois ||= Hash.new
1315           @whois[:nick] = argv[1]
1316           @whois[:operator] = argv[-1]
1317           # TODO update user info
1318         when RPL_WHOISIDLE
1319           @whois ||= Hash.new
1320           @whois[:nick] = argv[1]
1321           user = @server.user(@whois[:nick])
1322           @whois[:idle] = argv[2].to_i
1323           user.idle_since = Time.now - @whois[:idle]
1324           if argv[-1] == 'seconds idle, signon time'
1325             @whois[:signon] = Time.at(argv[3].to_i)
1326             user.signon = @whois[:signon]
1327           end
1328         when RPL_ENDOFWHOIS
1329           @whois ||= Hash.new
1330           @whois[:nick] = argv[1]
1331           data[:whois] = @whois.dup
1332           @whois.clear
1333           handle(:whois, data)
1334         when RPL_WHOISCHANNELS
1335           @whois ||= Hash.new
1336           @whois[:nick] = argv[1]
1337           @whois[:channels] ||= []
1338           user = @server.user(@whois[:nick])
1339           argv[-1].split.each do |prechan|
1340             pfx = prechan.scan(/[#{@server.supports[:prefix][:prefixes].join}]/)
1341             modes = pfx.map { |p| @server.mode_for_prefix p }
1342             chan = prechan[pfx.length..prechan.length]
1343
1344             channel = @server.channel(chan)
1345             channel.add_user(user, :silent => true)
1346             modes.map { |mode| channel.mode[mode].set(user) }
1347
1348             @whois[:channels] << [chan, modes]
1349           end
1350         when RPL_LISTSTART
1351           # ignore
1352         when RPL_LIST
1353           @list ||= Hash.new
1354           chan = argv[1]
1355           users = argv[2]
1356           topic = argv[3]
1357           @list[chan] = ListData.new(chan, users, topic)
1358         when RPL_LISTEND
1359           @list ||= Hash.new
1360           data[:list] = @list
1361           handle(:list, data)
1362         when RPL_CHANNELMODEIS
1363           parse_mode(serverstring, argv[1..-1], data)
1364           handle(:mode, data)
1365         when RPL_CREATIONTIME
1366           data[:channel] = @server.channel(argv[1])
1367           data[:time] = Time.at(argv[2].to_i)
1368           data[:channel].creation_time=data[:time]
1369           handle(:creationtime, data)
1370         when RPL_CHANNEL_URL
1371           data[:channel] = @server.channel(argv[1])
1372           data[:url] = argv[2]
1373           data[:channel].url=data[:url].dup
1374           handle(:channel_url, data)
1375         when ERR_NOSUCHNICK
1376           data[:target] = argv[1]
1377           data[:message] = argv[2]
1378           handle(:nosuchtarget, data)
1379           if user = @server.get_user(data[:target])
1380             @server.delete_user(user)
1381           end
1382         when ERR_NOSUCHCHANNEL
1383           data[:target] = argv[1]
1384           data[:message] = argv[2]
1385           handle(:nosuchtarget, data)
1386           if channel = @server.get_channel(data[:target])
1387             @server.delete_channel(channel)
1388           end
1389         else
1390           warning "Unknown message #{serverstring.inspect}"
1391           handle(:unknown, data)
1392         end
1393         return # We've processed the numeric reply
1394       end
1395
1396       # Otherwise, the command should be a single word
1397       case command.to_sym
1398       when :PING
1399         data[:pingid] = argv[0]
1400         handle(:ping, data)
1401       when :PONG
1402         data[:pingid] = argv[0]
1403         handle(:pong, data)
1404       when :PRIVMSG
1405         # you can either bind to 'PRIVMSG', to get every one and
1406         # parse it yourself, or you can bind to 'MSG', 'PUBLIC',
1407         # etc and get it all nicely split up for you.
1408
1409         begin
1410           data[:target] = @server.user_or_channel(argv[0])
1411         rescue
1412           # The previous may fail e.g. when the target is a server or something
1413           # like that (e.g. $<mask>). In any of these cases, we just use the
1414           # String as a target
1415           # FIXME we probably want to explicitly check for the #<mask> $<mask>
1416           data[:target] = argv[0]
1417         end
1418         data[:message] = argv[1]
1419         handle(:privmsg, data)
1420
1421         # Now we split it
1422         if data[:target].kind_of?(Channel)
1423           handle(:public, data)
1424         else
1425           handle(:msg, data)
1426         end
1427       when :NOTICE
1428         begin
1429           data[:target] = @server.user_or_channel(argv[0])
1430         rescue
1431           # The previous may fail e.g. when the target is a server or something
1432           # like that (e.g. $<mask>). In any of these cases, we just use the
1433           # String as a target
1434           # FIXME we probably want to explicitly check for the #<mask> $<mask>
1435           data[:target] = argv[0]
1436         end
1437         data[:message] = argv[1]
1438         case data[:source]
1439         when User
1440           handle(:notice, data)
1441         else
1442           # "server notice" (not from user, noone to reply to)
1443           handle(:snotice, data)
1444         end
1445       when :KICK
1446         data[:channel] = @server.channel(argv[0])
1447         data[:target] = @server.user(argv[1])
1448         data[:message] = argv[2]
1449
1450         @server.delete_user_from_channel(data[:target], data[:channel])
1451         if data[:target] == @user
1452           @server.delete_channel(data[:channel])
1453         end
1454
1455         handle(:kick, data)
1456       when :PART
1457         data[:channel] = @server.channel(argv[0])
1458         data[:message] = argv[1]
1459
1460         @server.delete_user_from_channel(data[:source], data[:channel])
1461         if data[:source] == @user
1462           @server.delete_channel(data[:channel])
1463         end
1464
1465         handle(:part, data)
1466       when :QUIT
1467         data[:message] = argv[0]
1468         data[:was_on] = @server.channels.inject(ChannelList.new) { |list, ch|
1469           list << ch if ch.has_user?(data[:source])
1470           list
1471         }
1472
1473         @server.delete_user(data[:source])
1474
1475         handle(:quit, data)
1476       when :JOIN
1477         data[:channel] = @server.channel(argv[0])
1478         data[:channel].add_user(data[:source])
1479
1480         handle(:join, data)
1481       when :TOPIC
1482         data[:channel] = @server.channel(argv[0])
1483         data[:topic] = Channel::Topic.new(argv[1], data[:source], Time.new)
1484         data[:channel].topic.replace(data[:topic])
1485
1486         handle(:changetopic, data)
1487       when :INVITE
1488         data[:target] = @server.user(argv[0])
1489         data[:channel] = @server.channel(argv[1])
1490
1491         handle(:invite, data)
1492       when :NICK
1493         data[:is_on] = @server.channels.inject(ChannelList.new) { |list, ch|
1494           list << ch if ch.has_user?(data[:source])
1495           list
1496         }
1497
1498         data[:newnick] = argv[0]
1499         data[:oldnick] = data[:source].nick.dup
1500         data[:source].nick = data[:newnick]
1501
1502         debug "#{data[:oldnick]} (now #{data[:newnick]}) was on #{data[:is_on].join(', ')}"
1503
1504         handle(:nick, data)
1505       when :MODE
1506         parse_mode(serverstring, argv, data)
1507         handle(:mode, data)
1508       when :ERROR
1509         data[:message] = argv[1]
1510         handle(:error, data)
1511       else
1512         warning "Unknown message #{serverstring.inspect}"
1513         handle(:unknown, data)
1514       end
1515     end
1516
1517     private
1518
1519     # key::  server event name
1520     # data:: hash containing data about the event, passed to the proc
1521     # call client's proc for an event, if they set one as a handler
1522     def handle(key, data)
1523       if(@handlers.has_key?(key))
1524         @handlers[key].call(data)
1525       end
1526     end
1527
1528     # RPL_CHANNELMODEIS
1529     # MODE ([+-]<modes> (<params>)*)*
1530     # When a MODE message is received by a server,
1531     # Type C will have parameters too, so we must
1532     # be able to consume parameters for all
1533     # but Type D modes
1534     def parse_mode(serverstring, argv, data)
1535       data[:target] = @server.user_or_channel(argv[0])
1536       data[:modestring] = argv[1..-1].join(" ")
1537       # data[:modes] is an array where each element
1538       # is an array with two elements, the first of which
1539       # is either :set or :reset, and the second symbol
1540       # is the mode letter. An optional third element
1541       # is present e.g. for channel modes that need
1542       # a parameter
1543       data[:modes] = []
1544       case data[:target]
1545       when User
1546         # User modes aren't currently handled internally,
1547         # but we still parse them and delegate to the client
1548         warning "Unhandled user mode message '#{serverstring}'"
1549         argv[1..-1].each { |arg|
1550           setting = arg[0].chr
1551           if "+-".include?(setting)
1552             setting = setting == "+" ? :set : :reset
1553             arg[1..-1].each_byte { |b|
1554               m = b.chr.intern
1555               data[:modes] << [setting, m]
1556             }
1557           else
1558             # Although typically User modes don't take an argument,
1559             # this is not true for all modes on all servers. Since
1560             # we have no knowledge of which modes take parameters
1561             # and which don't we just assign it to the last
1562             # mode. This is not going to do strange things often,
1563             # as usually User modes are only set one at a time
1564             warning "Unhandled user mode parameter #{arg} found"
1565             data[:modes].last << arg
1566           end
1567         }
1568       when Channel
1569         # array of indices in data[:modes] where parameters
1570         # are needed
1571         who_wants_params = []
1572
1573         modes = argv[1..-1].dup
1574         debug modes
1575         getting_args = false
1576         while arg = modes.shift
1577           debug arg
1578           if getting_args
1579             # getting args for previously set modes
1580             idx = who_wants_params.shift
1581             if idx.nil?
1582               warning "Oops, problems parsing #{serverstring.inspect}"
1583               break
1584             end
1585             data[:modes][idx] << arg
1586             getting_args = false if who_wants_params.empty?
1587           else
1588             debug @server.supports[:chanmodes]
1589             setting = :set
1590             arg.each_byte do |c|
1591               m = c.chr.intern
1592               case m
1593               when :+
1594                 setting = :set
1595               when :-
1596                 setting = :reset
1597               else
1598                 data[:modes] << [setting, m]
1599                 case m
1600                 when *@server.supports[:chanmodes][:typea]
1601                   who_wants_params << data[:modes].length - 1
1602                 when *@server.supports[:chanmodes][:typeb]
1603                   who_wants_params << data[:modes].length - 1
1604                 when *@server.supports[:chanmodes][:typec]
1605                   if setting == :set
1606                     who_wants_params << data[:modes].length - 1
1607                   end
1608                 when *@server.supports[:chanmodes][:typed]
1609                   # Nothing to do
1610                 when *@server.supports[:prefix][:modes]
1611                   who_wants_params << data[:modes].length - 1
1612                 else
1613                   warning "Ignoring unknown mode #{m} in #{serverstring.inspect}"
1614                   data[:modes].pop
1615                 end
1616               end
1617             end
1618             getting_args = true unless who_wants_params.empty?
1619           end
1620         end
1621         unless who_wants_params.empty?
1622           warning "Unhandled malformed modeline #{data[:modestring]} (unexpected empty arguments)"
1623           return
1624         end
1625
1626         data[:modes].each { |mode|
1627           set, key, val = mode
1628           if val
1629             data[:target].mode[key].send(set, val)
1630           else
1631             data[:target].mode[key].send(set)
1632           end
1633         }
1634       else
1635         warning "Ignoring #{data[:modestring]} for unrecognized target #{argv[0]} (#{data[:target].inspect})"
1636       end
1637     end
1638   end
1639 end