]>
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
}
620 #if WE_HAVE_A_REAL_CAPABILITY_NOW
624 0, MAXPARA
, 0, 0, NULL
,
625 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
626 { m_cap
, m_cap
, m_ignore
, m_cap
, m_ignore
}
629 /* This command is an alias for QUIT during the unregistered part of
630 * of the server. This is because someone jumping via a broken web
631 * proxy will send a 'POST' as their first command - which we will
632 * obviously disconnect them immediately for, stopping people abusing
638 0, MAXPARA
, MFLG_SLOW
, 0, NULL
,
639 /* UNREG, CLIENT, SERVER, OPER, SERVICE */
640 { m_quit
, m_ignore
, m_ignore
, m_ignore
, m_ignore
}
645 /** Array of command parameters. */
646 static char *para
[MAXPARA
+ 2]; /* leave room for prefix and null */
649 /** Add a message to the lookup trie.
650 * @param[in,out] mtree_p Trie node to insert under.
651 * @param[in] msg_p Message to insert.
652 * @param[in] cmd Text of command to insert.
655 add_msg_element(struct MessageTree
*mtree_p
, struct Message
*msg_p
, char *cmd
)
657 struct MessageTree
*ntree_p
;
661 mtree_p
->msg
= msg_p
;
665 if ((ntree_p
= mtree_p
->pointers
[*cmd
& (MAXPTRLEN
-1)]) != NULL
)
667 add_msg_element(ntree_p
, msg_p
, cmd
+1);
671 ntree_p
= (struct MessageTree
*)MyCalloc(sizeof(struct MessageTree
), 1);
672 mtree_p
->pointers
[*cmd
& (MAXPTRLEN
-1)] = ntree_p
;
673 add_msg_element(ntree_p
, msg_p
, cmd
+1);
677 /** Remove a message from the lookup trie.
678 * @param[in,out] mtree_p Trie node to remove command from.
679 * @param[in] cmd Text of command to remove.
682 del_msg_element(struct MessageTree
*mtree_p
, char *cmd
)
684 int slot
= *cmd
& (MAXPTRLEN
-1);
686 /* Either remove leaf message or from appropriate child. */
690 mtree_p
->pointers
[slot
] = del_msg_element(mtree_p
->pointers
[slot
], cmd
+ 1);
692 /* If current message or any child still exists, keep this node. */
695 for (slot
= 0; slot
< MAXPTRLEN
; ++slot
)
696 if (mtree_p
->pointers
[slot
])
699 /* Otherwise, if we're not a root node, free it and return null. */
700 if (mtree_p
!= &msg_tree
&& mtree_p
!= &tok_tree
)
705 /** Initialize the message lookup trie with all known commands. */
711 memset(&msg_tree
, 0, sizeof(msg_tree
));
712 memset(&tok_tree
, 0, sizeof(tok_tree
));
714 for (i
= 0; msgtab
[i
].cmd
!= NULL
; i
++)
716 add_msg_element(&msg_tree
, &msgtab
[i
], msgtab
[i
].cmd
);
717 add_msg_element(&tok_tree
, &msgtab
[i
], msgtab
[i
].tok
);
721 /** Look up a command in the message trie.
722 * @param cmd Text of command to look up.
723 * @param root Root of message trie.
724 * @return Pointer to matching message, or NULL if non exists.
726 static struct Message
*
727 msg_tree_parse(char *cmd
, struct MessageTree
*root
)
729 struct MessageTree
*mtree
;
731 for (mtree
= root
; mtree
; mtree
= mtree
->pointers
[(*cmd
++) & (MAXPTRLEN
-1)]) {
732 if (*cmd
== '\0' && mtree
->msg
)
734 else if (!IsAlpha(*cmd
))
740 /** Registers a service mapping to the pseudocommand handler.
741 * @param[in] map Service mapping to add.
742 * @return Non-zero on success; zero if a command already used the name.
744 int register_mapping(struct s_map
*map
)
748 if (msg_tree_parse(map
->command
, &msg_tree
))
751 msg
= (struct Message
*)MyMalloc(sizeof(struct Message
));
752 msg
->cmd
= map
->command
;
753 msg
->tok
= map
->command
;
756 msg
->flags
= MFLG_EXTRA
;
757 if (!(map
->flags
& SMAP_FAST
))
758 msg
->flags
|= MFLG_SLOW
;
762 msg
->handlers
[UNREGISTERED_HANDLER
] = m_ignore
;
763 msg
->handlers
[CLIENT_HANDLER
] = m_pseudo
;
764 msg
->handlers
[SERVER_HANDLER
] = m_ignore
;
765 msg
->handlers
[OPER_HANDLER
] = m_pseudo
;
766 msg
->handlers
[SERVICE_HANDLER
] = m_ignore
;
768 add_msg_element(&msg_tree
, msg
, msg
->cmd
);
774 /** Removes a service mapping.
775 * @param[in] map Service mapping to remove.
776 * @return Non-zero on success; zero if no command used the name.
778 int unregister_mapping(struct s_map
*map
)
780 if (!msg_tree_parse(map
->command
, &msg_tree
))
782 /* This simply should never happen. */
787 del_msg_element(&msg_tree
, map
->msg
->cmd
);
789 map
->msg
->extra
= NULL
;
796 /** Parse a line of data from a user.
797 * NOTE: parse_*() should not be called recursively by any other
799 * @param[in] cptr Client that sent the data.
800 * @param[in] buffer Start of input line.
801 * @param[in] bufend End of input line.
802 * @return 0 on success, -1 on parse error, or CPTR_KILLED if message
803 * handler returns it.
806 parse_client(struct Client
*cptr
, char *buffer
, char *bufend
)
808 struct Client
* from
= cptr
;
814 struct Message
* mptr
;
815 MessageHandler handler
= 0;
817 Debug((DEBUG_DEBUG
, "Client Parsing: %s", buffer
));
822 para
[0] = cli_name(from
);
823 for (ch
= buffer
; *ch
== ' '; ch
++); /* Eat leading spaces */
824 if (*ch
== ':') /* Is any client doing this ? */
826 for (++ch
; *ch
&& *ch
!= ' '; ++ch
)
827 ; /* Ignore sender prefix from client */
829 ch
++; /* Advance to command */
835 ServerStats
->is_empt
++;
836 Debug((DEBUG_NOTICE
, "Empty message from host %s:%s",
837 cli_name(cptr
), cli_name(from
)));
841 if ((s
= strchr(ch
, ' ')))
844 if ((mptr
= msg_tree_parse(ch
, &msg_tree
)) == NULL
)
847 * Note: Give error message *only* to recognized
848 * persons. It's a nightmare situation to have
849 * two programs sending "Unknown command"'s or
850 * equivalent to each other at full blast....
851 * If it has got to person state, it at least
852 * seems to be well behaving. Perhaps this message
853 * should never be generated, though... --msa
854 * Hm, when is the buffer empty -- if a command
855 * code has been found ?? -Armin
857 if (buffer
[0] != '\0')
860 send_reply(from
, ERR_UNKNOWNCOMMAND
, ch
);
861 Debug((DEBUG_ERROR
, "Unknown (%s) from %s",
862 ch
, get_client_name(cptr
, HIDE_IP
)));
864 ServerStats
->is_unco
++;
868 paramcount
= mptr
->parameters
;
869 i
= bufend
- ((s
) ? s
: ch
);
871 if ((mptr
->flags
& MFLG_SLOW
) || !IsAnOper(cptr
))
872 cli_since(cptr
) += (2 + i
/ 120);
874 * Allow only 1 msg per 2 seconds
875 * (on average) to prevent dumping.
876 * to keep the response rate up,
877 * bursts of up to 5 msgs are allowed
882 * Must the following loop really be so devious? On
883 * surface it splits the message to parameters from
884 * blank spaces. But, if paramcount has been reached,
885 * the rest of the message goes into this last parameter
886 * (about same effect as ":" has...) --msa
889 /* Note initially true: s==NULL || *(s-1) == '\0' !! */
891 if (mptr
->flags
& MFLG_EXTRA
) {
892 /* This is a horrid kludge to avoid changing the command handler
894 para
[1] = (char*)mptr
->extra
;
901 if (paramcount
> MAXPARA
)
902 paramcount
= MAXPARA
;
906 * Never "FRANCE " again!! ;-) Clean
907 * out *all* blanks.. --msa
917 * The rest is single parameter--can
918 * include blanks also.
926 for (; *s
!= ' ' && *s
; s
++);
932 handler
= mptr
->handlers
[cli_handler(cptr
)];
933 assert(0 != handler
);
935 if (!feature_bool(FEAT_IDLE_FROM_MSG
) && IsUser(cptr
) &&
936 handler
!= m_ping
&& handler
!= m_ignore
)
937 cli_user(from
)->last
= CurrentTime
;
939 return (*handler
) (cptr
, from
, i
, para
);
942 /** Parse a line of data from a server.
943 * @param[in] cptr Client that sent the data.
944 * @param[in] buffer Start of input line.
945 * @param[in] bufend End of input line.
946 * @return 0 on success, -1 on parse error, or CPTR_KILLED if message
947 * handler returns it.
949 int parse_server(struct Client
*cptr
, char *buffer
, char *bufend
)
951 struct Client
* from
= cptr
;
958 struct Message
* mptr
;
960 Debug((DEBUG_DEBUG
, "Server Parsing: %s", buffer
));
965 para
[0] = cli_name(from
);
968 * A server ALWAYS sends a prefix. When it starts with a ':' it's the
969 * protocol 9 prefix: a nick or a server name. Otherwise it's a numeric
974 /* Let para[0] point to the name of the sender */
976 if (!(ch
= strchr(ch
, ' ')))
980 /* And let `from' point to its client structure,
981 opps.. a server is _also_ a client --Nem */
982 from
= FindClient(para
[0]);
985 * If the client corresponding to the
986 * prefix is not found. We must ignore it,
987 * it is simply a lagged message traveling
988 * upstream a SQUIT that removed the client
993 Debug((DEBUG_NOTICE
, "Unknown prefix (%s)(%s) from (%s)",
994 para
[0], buffer
, cli_name(cptr
)));
995 ++ServerStats
->is_unpf
;
999 * However, the only thing that MUST be
1000 * allowed to travel upstream against an
1001 * squit, is an SQUIT itself (the timestamp
1002 * protects us from being used wrong)
1006 para
[0] = cli_name(cptr
);
1012 else if (cli_from(from
) != cptr
)
1014 ++ServerStats
->is_wrdi
;
1015 Debug((DEBUG_NOTICE
, "Fake direction: Message (%s) coming from (%s)",
1016 buffer
, cli_name(cptr
)));
1022 char numeric_prefix
[6];
1024 for (i
= 0; i
< 5; ++i
)
1026 if ('\0' == ch
[i
] || ' ' == (numeric_prefix
[i
] = ch
[i
]))
1031 numeric_prefix
[i
] = '\0';
1034 * We got a numeric nick as prefix
1035 * 1 or 2 character prefixes are from servers
1036 * 3 or 5 chars are from clients
1040 protocol_violation(cptr
,"Missing Prefix");
1043 else if (' ' == ch
[1] || ' ' == ch
[2])
1044 from
= FindNServer(numeric_prefix
);
1046 from
= findNUser(numeric_prefix
);
1052 while (*ch
!= ' ' && *ch
);
1055 * If the client corresponding to the
1056 * prefix is not found. We must ignore it,
1057 * it is simply a lagged message traveling
1058 * upstream a SQUIT that removed the client
1060 * There turned out to be other reasons that
1061 * a prefix is unknown, needing an upstream
1062 * KILL. Also, next to an SQUIT we better
1063 * allow a KILL to pass too.
1068 ServerStats
->is_unpf
++;
1071 if (*ch
== 'N' && (ch
[1] == ' ' || ch
[1] == 'I'))
1072 /* Only sent a KILL for a nick change */
1074 struct Client
*server
;
1075 /* Kill the unknown numeric prefix upstream if
1076 * it's server still exists: */
1077 if ((server
= FindNServer(numeric_prefix
)) && cli_from(server
) == cptr
)
1078 sendcmdto_one(&me
, CMD_KILL
, cptr
, "%s :%s (Unknown numeric nick)",
1079 numeric_prefix
, cli_name(&me
));
1082 * Things that must be allowed to travel
1083 * upstream against an squit:
1085 if (ch
[1] == 'Q' || (*ch
== 'D' && ch
[1] == ' ') ||
1086 (*ch
== 'K' && ch
[2] == 'L'))
1092 /* Let para[0] point to the name of the sender */
1093 para
[0] = cli_name(from
);
1095 if (cli_from(from
) != cptr
)
1097 ServerStats
->is_wrdi
++;
1098 Debug((DEBUG_NOTICE
, "Fake direction: Message (%s) coming from (%s)",
1099 buffer
, cli_name(cptr
)));
1108 ServerStats
->is_empt
++;
1109 Debug((DEBUG_NOTICE
, "Empty message from host %s:%s",
1110 cli_name(cptr
), cli_name(from
)));
1115 * Extract the command code from the packet. Point s to the end
1116 * of the command code and calculate the length using pointer
1117 * arithmetic. Note: only need length for numerics and *all*
1118 * numerics must have parameters and thus a space after the command
1121 s
= strchr(ch
, ' '); /* s -> End of the command code */
1122 len
= (s
) ? (s
- ch
) : 0;
1123 if (len
== 3 && IsDigit(*ch
))
1125 numeric
= (*ch
- '0') * 100 + (*(ch
+ 1) - '0') * 10 + (*(ch
+ 2) - '0');
1126 paramcount
= 2; /* destination, and the rest of it */
1127 ServerStats
->is_num
++;
1128 mptr
= NULL
; /* Init. to avoid stupid compiler warning :/ */
1135 /* Version Receive Send
1137 * 2.10.0 Tkn/Long Long
1138 * 2.10.10 Tkn/Long Tkn
1141 * Clients/unreg servers always receive/
1142 * send long commands -record
1144 * And for the record, this trie parser really does not care. - Dianora
1147 mptr
= msg_tree_parse(ch
, &tok_tree
);
1151 mptr
= msg_tree_parse(ch
, &msg_tree
);
1157 * Note: Give error message *only* to recognized
1158 * persons. It's a nightmare situation to have
1159 * two programs sending "Unknown command"'s or
1160 * equivalent to each other at full blast....
1161 * If it has got to person state, it at least
1162 * seems to be well behaving. Perhaps this message
1163 * should never be generated, though... --msa
1164 * Hm, when is the buffer empty -- if a command
1165 * code has been found ?? -Armin
1168 if (buffer
[0] != '\0')
1170 Debug((DEBUG_ERROR
, "Unknown (%s) from %s",
1171 ch
, get_client_name(cptr
, HIDE_IP
)));
1174 ServerStats
->is_unco
++;
1178 paramcount
= mptr
->parameters
;
1179 i
= bufend
- ((s
) ? s
: ch
);
1183 * Must the following loop really be so devious? On
1184 * surface it splits the message to parameters from
1185 * blank spaces. But, if paramcount has been reached,
1186 * the rest of the message goes into this last parameter
1187 * (about same effect as ":" has...) --msa
1190 /* Note initially true: s==NULL || *(s-1) == '\0' !! */
1195 if (paramcount
> MAXPARA
)
1196 paramcount
= MAXPARA
;
1200 * Never "FRANCE " again!! ;-) Clean
1201 * out *all* blanks.. --msa
1211 * The rest is single parameter--can
1212 * include blanks also.
1215 para
[++i
] = s
; /* preserve the colon to make do_numeric happy */
1221 if (i
>= paramcount
)
1223 for (; *s
!= ' ' && *s
; s
++);
1228 return (do_numeric(numeric
, (*buffer
!= ':'), cptr
, from
, i
, para
));
1231 return (*mptr
->handlers
[cli_handler(cptr
)]) (cptr
, from
, i
, para
);