]>
jfr.im git - irc/quakenet/snircd.git/blob - ircd/parse.c
2 * IRC - Internet Relay Chat, common/parse.c
3 * Copyright (C) 1990 Jarkko Oikarinen and
4 * University of Oulu, Computing Center
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 1, or (at your option)
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * @brief Parse input from IRC clients and other servers.
22 * @version $Id: parse.c,v 1.54 2005/09/27 03:54:46 entrope Exp $
32 #include "ircd_alloc.h"
33 #include "ircd_chattr.h"
34 #include "ircd_features.h"
36 #include "ircd_reply.h"
37 #include "ircd_string.h"
42 #include "querycmds.h"
48 #include "s_numeric.h"
56 /* #include <assert.h> -- Now using assert in ircd_log.h */
61 * Message Tree stuff mostly written by orabidoo, with changes by Dianora.
62 * Adapted to Undernet, adding token support, etc by comstud 10/06/97
64 * completely rewritten June 2, 2003 - Dianora
66 * This has always just been a trie. Look at volume III of Knuth ACP
69 * ok, you start out with an array of pointers, each one corresponds
70 * to a letter at the current position in the command being examined.
72 * so roughly you have this for matching 'trie' or 'tie'
74 * 't' points -> [MessageTree *] 'r' -> [MessageTree *] -> 'i'
75 * -> [MessageTree *] -> [MessageTree *] -> 'e' and matches
77 * 'i' -> [MessageTree *] -> 'e' and matches
80 /** Number of children under a trie node. */
81 #define MAXPTRLEN 32 /* Must be a power of 2, and
82 * larger than 26 [a-z]|[A-Z]
83 * its used to allocate the set
84 * of pointers at each node of the tree
85 * There are MAXPTRLEN pointers at each node.
86 * Obviously, there have to be more pointers
87 * Than ASCII letters. 32 is a nice number
88 * since there is then no need to shift
89 * 'A'/'a' to base 0 index, at the expense
90 * of a few never used pointers. For a small
91 * parser like this, this is a good compromise
92 * and does make it somewhat faster.
97 /** Node in the command lookup trie. */
99 struct Message
*msg
; /**< Message (if any) if the string ends now. */
100 struct MessageTree
*pointers
[MAXPTRLEN
]; /**< Child nodes for each letter. */
103 /** Root of command lookup trie. */
104 static struct MessageTree msg_tree
;
105 static struct MessageTree tok_tree
;
107 /** Array of all supported commands. */
108 struct Message msgtab
[] = {
112 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
113 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
114 { m_unregistered
, m_privmsg
, ms_privmsg
, mo_privmsg
, m_ignore
}
119 0, MAXPARA
, MFLG_SLOW
| MFLG_UNREG
, 0, NULL
,
120 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
121 { m_nick
, m_nick
, ms_nick
, m_nick
, m_ignore
}
126 0, MAXPARA
, MFLG_SLOW
| MFLG_IGNORE
, 0, NULL
,
127 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
128 { m_ignore
, m_notice
, ms_notice
, mo_notice
, m_ignore
}
133 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
134 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
135 { m_unregistered
, m_wallchops
, ms_wallchops
, m_wallchops
, m_ignore
}
140 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
141 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
142 { m_unregistered
, m_wallvoices
, ms_wallvoices
, m_wallvoices
, m_ignore
}
147 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
148 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
149 { m_unregistered
, m_cprivmsg
, m_ignore
, m_cprivmsg
, m_ignore
}
154 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
155 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
156 { m_unregistered
, m_cnotice
, m_ignore
, m_cnotice
, m_ignore
}
161 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
162 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
163 { m_unregistered
, m_join
, ms_join
, m_join
, m_ignore
}
168 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
169 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
170 { m_unregistered
, m_mode
, ms_mode
, m_mode
, m_ignore
}
175 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
176 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
177 { m_ignore
, m_ignore
, ms_burst
, m_ignore
, m_ignore
}
182 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
183 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
184 { m_ignore
, m_ignore
, ms_create
, m_ignore
, m_ignore
}
189 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
190 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
191 { m_ignore
, m_ignore
, ms_destruct
, m_ignore
, m_ignore
}
196 0, MAXPARA
, MFLG_SLOW
| MFLG_UNREG
, 0, NULL
,
197 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
198 { m_quit
, m_quit
, ms_quit
, m_quit
, m_ignore
}
203 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
204 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
205 { m_unregistered
, m_part
, ms_part
, m_part
, m_ignore
}
210 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
211 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
212 { m_unregistered
, m_topic
, ms_topic
, m_topic
, m_ignore
}
217 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
218 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
219 { m_unregistered
, m_invite
, ms_invite
, m_invite
, m_ignore
}
224 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
225 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
226 { m_unregistered
, m_kick
, ms_kick
, m_kick
, m_ignore
}
231 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
232 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
233 { m_unregistered
, m_not_oper
, ms_wallops
, mo_wallops
, m_ignore
}
238 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
239 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
240 { m_unregistered
, m_not_oper
, ms_wallusers
, mo_wallusers
, m_ignore
}
245 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
246 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
247 { m_ignore
, m_ignore
, ms_desynch
, m_ignore
, m_ignore
}
252 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
253 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
254 { m_unregistered
, m_ping
, ms_ping
, mo_ping
, m_ignore
}
259 0, MAXPARA
, MFLG_SLOW
| MFLG_UNREG
, 0, NULL
,
260 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
261 { mr_pong
, m_pong
, ms_pong
, m_pong
, m_ignore
}
266 0, MAXPARA
, MFLG_SLOW
| MFLG_UNREG
, 0, NULL
,
267 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
268 { mr_error
, m_ignore
, ms_error
, m_ignore
, m_ignore
}
273 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
274 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
275 { m_unregistered
, m_not_oper
, ms_kill
, mo_kill
, m_ignore
}
280 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
281 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
282 { m_user
, m_registered
, m_ignore
, m_registered
, m_ignore
}
287 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
288 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
289 { m_unregistered
, m_away
, ms_away
, m_away
, m_ignore
}
294 0, 1, MFLG_SLOW
, 0, NULL
,
295 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
296 { m_unregistered
, m_ison
, m_ignore
, m_ison
, m_ignore
}
301 0, MAXPARA
, MFLG_SLOW
| MFLG_UNREG
, 0, NULL
,
302 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
303 { mr_server
, m_registered
, ms_server
, m_registered
, m_ignore
}
308 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
309 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
310 { m_unregistered
, m_not_oper
, ms_squit
, mo_squit
, m_ignore
}
315 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
316 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
317 { m_unregistered
, m_whois
, ms_whois
, m_whois
, m_ignore
}
322 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
323 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
324 { m_unregistered
, m_who
, m_ignore
, m_who
, m_ignore
}
329 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
330 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
331 { m_unregistered
, m_whowas
, m_whowas
, m_whowas
, m_ignore
}
336 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
337 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
338 { m_unregistered
, m_list
, m_ignore
, m_list
, m_ignore
}
343 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
344 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
345 { m_unregistered
, m_names
, ms_names
, m_names
, m_ignore
}
350 0, 1, MFLG_SLOW
, 0, NULL
,
351 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
352 { m_unregistered
, m_userhost
, m_ignore
, m_userhost
, m_ignore
}
357 0, 1, MFLG_SLOW
, 0, NULL
,
358 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
359 { m_unregistered
, m_userip
, m_ignore
, m_userip
, m_ignore
}
364 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
365 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
366 { m_unregistered
, m_trace
, ms_trace
, mo_trace
, m_ignore
}
371 0, MAXPARA
, MFLG_SLOW
| MFLG_UNREG
, 0, NULL
,
372 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
373 { mr_pass
, m_registered
, m_ignore
, m_registered
, m_ignore
}
378 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
379 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
380 { m_unregistered
, m_lusers
, ms_lusers
, m_lusers
, m_ignore
}
385 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
386 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
387 { m_unregistered
, m_time
, m_time
, m_time
, m_ignore
}
392 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
393 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
394 { m_unregistered
, m_ignore
, ms_settime
, mo_settime
, m_ignore
}
399 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
400 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
401 { m_unregistered
, m_not_oper
, ms_rping
, mo_rping
, m_ignore
}
406 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
407 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
408 { m_unregistered
, m_ignore
, ms_rpong
, m_ignore
, m_ignore
}
413 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
414 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
415 { m_unregistered
, m_oper
, ms_oper
, mo_oper
, m_ignore
}
420 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
421 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
422 { m_unregistered
, m_not_oper
, ms_connect
, mo_connect
, m_ignore
}
427 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
428 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
429 { m_unregistered
, m_map
, m_ignore
, m_map
, m_ignore
}
434 0, MAXPARA
, MFLG_SLOW
| MFLG_UNREG
, 0, NULL
,
435 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
436 { m_version
, m_version
, ms_version
, mo_version
, m_ignore
}
441 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
442 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
443 { m_unregistered
, m_stats
, m_stats
, m_stats
, m_ignore
}
448 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
449 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
450 { m_unregistered
, m_links
, ms_links
, m_links
, m_ignore
}
455 0, MAXPARA
, MFLG_SLOW
| MFLG_UNREG
, 0, NULL
,
456 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
457 { m_admin
, m_admin
, ms_admin
, mo_admin
, m_ignore
}
462 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
463 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
464 { m_unregistered
, m_help
, m_ignore
, m_help
, m_ignore
}
469 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
470 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
471 { m_unregistered
, m_info
, ms_info
, mo_info
, m_ignore
}
476 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
477 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
478 { m_unregistered
, m_motd
, m_motd
, m_motd
, m_ignore
}
483 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
484 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
485 { m_unregistered
, m_not_oper
, m_ignore
, mo_close
, m_ignore
}
490 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
491 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
492 { m_unregistered
, m_silence
, ms_silence
, m_silence
, m_ignore
}
497 0, MAXPARA
, 0, 0, NULL
,
498 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
499 { m_unregistered
, m_gline
, ms_gline
, mo_gline
, m_ignore
}
504 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
505 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
506 { m_unregistered
, m_not_oper
, ms_jupe
, mo_jupe
, m_ignore
}
511 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
512 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
513 { m_unregistered
, m_not_oper
, ms_opmode
, mo_opmode
, m_ignore
}
518 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
519 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
520 { m_unregistered
, m_not_oper
, ms_clearmode
, mo_clearmode
, m_ignore
}
525 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
526 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
527 { m_unregistered
, m_not_oper
, ms_uping
, mo_uping
, m_ignore
}
532 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
533 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
534 { m_ignore
, m_ignore
, ms_end_of_burst
, m_ignore
, m_ignore
}
537 MSG_END_OF_BURST_ACK
,
538 TOK_END_OF_BURST_ACK
,
539 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
540 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
541 { m_ignore
, m_ignore
, ms_end_of_burst_ack
, m_ignore
, m_ignore
}
546 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
547 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
548 { m_unregistered
, m_hash
, m_hash
, m_hash
, m_ignore
}
553 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
554 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
555 { m_unregistered
, m_not_oper
, m_ignore
, mo_rehash
, m_ignore
}
560 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
561 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
562 { m_unregistered
, m_not_oper
, m_ignore
, mo_restart
, m_ignore
}
567 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
568 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
569 { m_unregistered
, m_not_oper
, m_ignore
, mo_die
, m_ignore
}
574 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
575 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
576 { m_proto
, m_proto
, m_proto
, m_proto
, m_ignore
}
581 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
582 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
583 { m_unregistered
, m_not_oper
, m_ignore
, mo_set
, m_ignore
}
588 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
589 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
590 { m_unregistered
, m_not_oper
, m_ignore
, mo_reset
, m_ignore
}
595 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
596 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
597 { m_unregistered
, m_not_oper
, m_ignore
, mo_get
, m_ignore
}
602 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
603 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
604 { m_unregistered
, m_not_oper
, ms_privs
, mo_privs
, m_ignore
}
609 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
610 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
611 { m_ignore
, m_ignore
, ms_account
, m_ignore
, m_ignore
}
616 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
617 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
618 { m_ignore
, m_not_oper
, ms_asll
, mo_asll
, m_ignore
}
623 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
624 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
625 { m_unregistered
, m_sethost
, m_ignore
, m_sethost
, m_ignore
}
627 #if WE_HAVE_A_REAL_CAPABILITY_NOW
631 0, MAXPARA
, 0, 0, NULL
,
632 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
633 { m_cap
, m_cap
, m_ignore
, m_cap
, m_ignore
}
638 * - ASUKA ---------------------------------------------------------------------
639 * Add the command for CHECK.
640 * This was adapted from Lain for use in Asuka.
641 * Original code by Durzel (durzel@quakenet.org).
643 * qoreQ (qoreQ@quakenet.org) - 08/14/2002
644 * -----------------------------------------------------------------------------
649 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
650 { m_unregistered
, m_not_oper
, m_check
, m_check
, m_ignore
}
653 /* This command is an alias for QUIT during the unregistered part of
654 * of the server. This is because someone jumping via a broken web
655 * proxy will send a 'POST' as their first command - which we will
656 * obviously disconnect them immediately for, stopping people abusing
662 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
663 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
664 { m_quit
, m_ignore
, m_ignore
, m_ignore
, m_ignore
}
669 /** Array of command parameters. */
670 static char *para
[MAXPARA
+ 2]; /* leave room for prefix and null */
673 /** Add a message to the lookup trie.
674 * @param[in,out] mtree_p Trie node to insert under.
675 * @param[in] msg_p Message to insert.
676 * @param[in] cmd Text of command to insert.
679 add_msg_element(struct MessageTree
*mtree_p
, struct Message
*msg_p
, char *cmd
)
681 struct MessageTree
*ntree_p
;
685 mtree_p
->msg
= msg_p
;
689 if ((ntree_p
= mtree_p
->pointers
[*cmd
& (MAXPTRLEN
-1)]) != NULL
)
691 add_msg_element(ntree_p
, msg_p
, cmd
+1);
695 ntree_p
= (struct MessageTree
*)MyCalloc(sizeof(struct MessageTree
), 1);
696 mtree_p
->pointers
[*cmd
& (MAXPTRLEN
-1)] = ntree_p
;
697 add_msg_element(ntree_p
, msg_p
, cmd
+1);
701 /** Remove a message from the lookup trie.
702 * @param[in,out] mtree_p Trie node to remove command from.
703 * @param[in] cmd Text of command to remove.
706 del_msg_element(struct MessageTree
*mtree_p
, char *cmd
)
708 int slot
= *cmd
& (MAXPTRLEN
-1);
710 /* Either remove leaf message or from appropriate child. */
714 mtree_p
->pointers
[slot
] = del_msg_element(mtree_p
->pointers
[slot
], cmd
+ 1);
716 /* If current message or any child still exists, keep this node. */
719 for (slot
= 0; slot
< MAXPTRLEN
; ++slot
)
720 if (mtree_p
->pointers
[slot
])
723 /* Otherwise, if we're not a root node, free it and return null. */
724 if (mtree_p
!= &msg_tree
&& mtree_p
!= &tok_tree
)
729 /** Initialize the message lookup trie with all known commands. */
735 memset(&msg_tree
, 0, sizeof(msg_tree
));
736 memset(&tok_tree
, 0, sizeof(tok_tree
));
738 for (i
= 0; msgtab
[i
].cmd
!= NULL
; i
++)
740 add_msg_element(&msg_tree
, &msgtab
[i
], msgtab
[i
].cmd
);
741 add_msg_element(&tok_tree
, &msgtab
[i
], msgtab
[i
].tok
);
745 /** Look up a command in the message trie.
746 * @param cmd Text of command to look up.
747 * @param root Root of message trie.
748 * @return Pointer to matching message, or NULL if non exists.
750 static struct Message
*
751 msg_tree_parse(char *cmd
, struct MessageTree
*root
)
753 struct MessageTree
*mtree
;
755 for (mtree
= root
; mtree
; mtree
= mtree
->pointers
[(*cmd
++) & (MAXPTRLEN
-1)]) {
756 if (*cmd
== '\0' && mtree
->msg
)
758 else if (!IsAlpha(*cmd
))
764 /** Registers a service mapping to the pseudocommand handler.
765 * @param[in] map Service mapping to add.
766 * @return Non-zero on success; zero if a command already used the name.
768 int register_mapping(struct s_map
*map
)
772 if (msg_tree_parse(map
->command
, &msg_tree
))
775 msg
= (struct Message
*)MyMalloc(sizeof(struct Message
));
776 msg
->cmd
= map
->command
;
777 msg
->tok
= map
->command
;
780 msg
->flags
= MFLG_EXTRA
;
781 if (!(map
->flags
& SMAP_FAST
))
782 msg
->flags
|= MFLG_SLOW
;
786 msg
->handlers
[UNREGISTERED_HANDLER
] = m_ignore
;
787 msg
->handlers
[CLIENT_HANDLER
] = m_pseudo
;
788 msg
->handlers
[SERVER_HANDLER
] = m_ignore
;
789 msg
->handlers
[OPER_HANDLER
] = m_pseudo
;
790 msg
->handlers
[SERVICE_HANDLER
] = m_ignore
;
792 add_msg_element(&msg_tree
, msg
, msg
->cmd
);
798 /** Removes a service mapping.
799 * @param[in] map Service mapping to remove.
800 * @return Non-zero on success; zero if no command used the name.
802 int unregister_mapping(struct s_map
*map
)
804 if (!msg_tree_parse(map
->command
, &msg_tree
))
806 /* This simply should never happen. */
811 del_msg_element(&msg_tree
, map
->msg
->cmd
);
813 map
->msg
->extra
= NULL
;
820 /** Parse a line of data from a user.
821 * NOTE: parse_*() should not be called recursively by any other
823 * @param[in] cptr Client that sent the data.
824 * @param[in] buffer Start of input line.
825 * @param[in] bufend End of input line.
826 * @return 0 on success, -1 on parse error, or CPTR_KILLED if message
827 * handler returns it.
830 parse_client(struct Client
*cptr
, char *buffer
, char *bufend
)
832 struct Client
* from
= cptr
;
838 struct Message
* mptr
;
839 MessageHandler handler
= 0;
841 Debug((DEBUG_DEBUG
, "Client Parsing: %s", buffer
));
846 para
[0] = cli_name(from
);
847 for (ch
= buffer
; *ch
== ' '; ch
++); /* Eat leading spaces */
848 if (*ch
== ':') /* Is any client doing this ? */
850 for (++ch
; *ch
&& *ch
!= ' '; ++ch
)
851 ; /* Ignore sender prefix from client */
853 ch
++; /* Advance to command */
859 ServerStats
->is_empt
++;
860 Debug((DEBUG_NOTICE
, "Empty message from host %s:%s",
861 cli_name(cptr
), cli_name(from
)));
865 if ((s
= strchr(ch
, ' ')))
868 if ((mptr
= msg_tree_parse(ch
, &msg_tree
)) == NULL
)
871 * Note: Give error message *only* to recognized
872 * persons. It's a nightmare situation to have
873 * two programs sending "Unknown command"'s or
874 * equivalent to each other at full blast....
875 * If it has got to person state, it at least
876 * seems to be well behaving. Perhaps this message
877 * should never be generated, though... --msa
878 * Hm, when is the buffer empty -- if a command
879 * code has been found ?? -Armin
881 if (buffer
[0] != '\0')
884 send_reply(from
, ERR_UNKNOWNCOMMAND
, ch
);
885 Debug((DEBUG_ERROR
, "Unknown (%s) from %s",
886 ch
, get_client_name(cptr
, HIDE_IP
)));
888 ServerStats
->is_unco
++;
892 paramcount
= mptr
->parameters
;
893 i
= bufend
- ((s
) ? s
: ch
);
895 if ((mptr
->flags
& MFLG_SLOW
) || !IsAnOper(cptr
)) {
896 if (IsAnOper(cptr
)) {
897 cli_since(cptr
) += 1;
899 cli_since(cptr
) += (2 + i
/ 120);
904 * Allow only 1 msg per 2 seconds
905 * (on average) to prevent dumping.
906 * to keep the response rate up,
907 * bursts of up to 5 msgs are allowed
912 * Must the following loop really be so devious? On
913 * surface it splits the message to parameters from
914 * blank spaces. But, if paramcount has been reached,
915 * the rest of the message goes into this last parameter
916 * (about same effect as ":" has...) --msa
919 /* Note initially true: s==NULL || *(s-1) == '\0' !! */
921 if (mptr
->flags
& MFLG_EXTRA
) {
922 /* This is a horrid kludge to avoid changing the command handler
924 para
[1] = (char*)mptr
->extra
;
931 if (paramcount
> MAXPARA
)
932 paramcount
= MAXPARA
;
936 * Never "FRANCE " again!! ;-) Clean
937 * out *all* blanks.. --msa
947 * The rest is single parameter--can
948 * include blanks also.
956 for (; *s
!= ' ' && *s
; s
++);
962 handler
= mptr
->handlers
[cli_handler(cptr
)];
963 assert(0 != handler
);
965 if (!feature_bool(FEAT_IDLE_FROM_MSG
) && IsUser(cptr
) &&
966 handler
!= m_ping
&& handler
!= m_ignore
)
967 cli_user(from
)->last
= CurrentTime
;
969 return (*handler
) (cptr
, from
, i
, para
);
972 /** Parse a line of data from a server.
973 * @param[in] cptr Client that sent the data.
974 * @param[in] buffer Start of input line.
975 * @param[in] bufend End of input line.
976 * @return 0 on success, -1 on parse error, or CPTR_KILLED if message
977 * handler returns it.
979 int parse_server(struct Client
*cptr
, char *buffer
, char *bufend
)
981 struct Client
* from
= cptr
;
988 struct Message
* mptr
;
990 Debug((DEBUG_DEBUG
, "Server Parsing: %s", buffer
));
995 para
[0] = cli_name(from
);
998 * A server ALWAYS sends a prefix. When it starts with a ':' it's the
999 * protocol 9 prefix: a nick or a server name. Otherwise it's a numeric
1004 /* Let para[0] point to the name of the sender */
1006 if (!(ch
= strchr(ch
, ' ')))
1010 /* And let `from' point to its client structure,
1011 opps.. a server is _also_ a client --Nem */
1012 from
= FindClient(para
[0]);
1015 * If the client corresponding to the
1016 * prefix is not found. We must ignore it,
1017 * it is simply a lagged message traveling
1018 * upstream a SQUIT that removed the client
1023 Debug((DEBUG_NOTICE
, "Unknown prefix (%s)(%s) from (%s)",
1024 para
[0], buffer
, cli_name(cptr
)));
1025 ++ServerStats
->is_unpf
;
1029 * However, the only thing that MUST be
1030 * allowed to travel upstream against an
1031 * squit, is an SQUIT itself (the timestamp
1032 * protects us from being used wrong)
1036 para
[0] = cli_name(cptr
);
1042 else if (cli_from(from
) != cptr
)
1044 ++ServerStats
->is_wrdi
;
1045 Debug((DEBUG_NOTICE
, "Fake direction: Message (%s) coming from (%s)",
1046 buffer
, cli_name(cptr
)));
1052 char numeric_prefix
[6];
1054 for (i
= 0; i
< 5; ++i
)
1056 if ('\0' == ch
[i
] || ' ' == (numeric_prefix
[i
] = ch
[i
]))
1061 numeric_prefix
[i
] = '\0';
1064 * We got a numeric nick as prefix
1065 * 1 or 2 character prefixes are from servers
1066 * 3 or 5 chars are from clients
1070 protocol_violation(cptr
,"Missing Prefix");
1073 else if (' ' == ch
[1] || ' ' == ch
[2])
1074 from
= FindNServer(numeric_prefix
);
1076 from
= findNUser(numeric_prefix
);
1082 while (*ch
!= ' ' && *ch
);
1085 * If the client corresponding to the
1086 * prefix is not found. We must ignore it,
1087 * it is simply a lagged message traveling
1088 * upstream a SQUIT that removed the client
1090 * There turned out to be other reasons that
1091 * a prefix is unknown, needing an upstream
1092 * KILL. Also, next to an SQUIT we better
1093 * allow a KILL to pass too.
1098 ServerStats
->is_unpf
++;
1101 if (*ch
== 'N' && (ch
[1] == ' ' || ch
[1] == 'I'))
1102 /* Only sent a KILL for a nick change */
1104 struct Client
*server
;
1105 /* Kill the unknown numeric prefix upstream if
1106 * it's server still exists: */
1107 if ((server
= FindNServer(numeric_prefix
)) && cli_from(server
) == cptr
)
1108 sendcmdto_one(&me
, CMD_KILL
, cptr
, "%s :%s (Unknown numeric nick)",
1109 numeric_prefix
, cli_name(&me
));
1112 * Things that must be allowed to travel
1113 * upstream against an squit:
1115 if (ch
[1] == 'Q' || (*ch
== 'D' && ch
[1] == ' ') ||
1116 (*ch
== 'K' && ch
[2] == 'L'))
1122 /* Let para[0] point to the name of the sender */
1123 para
[0] = cli_name(from
);
1125 if (cli_from(from
) != cptr
)
1127 ServerStats
->is_wrdi
++;
1128 Debug((DEBUG_NOTICE
, "Fake direction: Message (%s) coming from (%s)",
1129 buffer
, cli_name(cptr
)));
1138 ServerStats
->is_empt
++;
1139 Debug((DEBUG_NOTICE
, "Empty message from host %s:%s",
1140 cli_name(cptr
), cli_name(from
)));
1145 * Extract the command code from the packet. Point s to the end
1146 * of the command code and calculate the length using pointer
1147 * arithmetic. Note: only need length for numerics and *all*
1148 * numerics must have parameters and thus a space after the command
1151 s
= strchr(ch
, ' '); /* s -> End of the command code */
1152 len
= (s
) ? (s
- ch
) : 0;
1153 if (len
== 3 && IsDigit(*ch
))
1155 numeric
= (*ch
- '0') * 100 + (*(ch
+ 1) - '0') * 10 + (*(ch
+ 2) - '0');
1156 paramcount
= 2; /* destination, and the rest of it */
1157 ServerStats
->is_num
++;
1158 mptr
= NULL
; /* Init. to avoid stupid compiler warning :/ */
1165 /* Version Receive Send
1167 * 2.10.0 Tkn/Long Long
1168 * 2.10.10 Tkn/Long Tkn
1171 * Clients/unreg servers always receive/
1172 * send long commands -record
1174 * And for the record, this trie parser really does not care. - Dianora
1177 mptr
= msg_tree_parse(ch
, &tok_tree
);
1181 mptr
= msg_tree_parse(ch
, &msg_tree
);
1187 * Note: Give error message *only* to recognized
1188 * persons. It's a nightmare situation to have
1189 * two programs sending "Unknown command"'s or
1190 * equivalent to each other at full blast....
1191 * If it has got to person state, it at least
1192 * seems to be well behaving. Perhaps this message
1193 * should never be generated, though... --msa
1194 * Hm, when is the buffer empty -- if a command
1195 * code has been found ?? -Armin
1198 if (buffer
[0] != '\0')
1200 Debug((DEBUG_ERROR
, "Unknown (%s) from %s",
1201 ch
, get_client_name(cptr
, HIDE_IP
)));
1204 ServerStats
->is_unco
++;
1208 paramcount
= mptr
->parameters
;
1209 i
= bufend
- ((s
) ? s
: ch
);
1213 * Must the following loop really be so devious? On
1214 * surface it splits the message to parameters from
1215 * blank spaces. But, if paramcount has been reached,
1216 * the rest of the message goes into this last parameter
1217 * (about same effect as ":" has...) --msa
1220 /* Note initially true: s==NULL || *(s-1) == '\0' !! */
1225 if (paramcount
> MAXPARA
)
1226 paramcount
= MAXPARA
;
1230 * Never "FRANCE " again!! ;-) Clean
1231 * out *all* blanks.. --msa
1241 * The rest is single parameter--can
1242 * include blanks also.
1245 para
[++i
] = s
; /* preserve the colon to make do_numeric happy */
1251 if (i
>= paramcount
)
1253 for (; *s
!= ' ' && *s
; s
++);
1258 return (do_numeric(numeric
, (*buffer
!= ':'), cptr
, from
, i
, para
));
1261 return (*mptr
->handlers
[cli_handler(cptr
)]) (cptr
, from
, i
, para
);