2 * ircd-ratbox: A slightly useful ircd.
3 * parse.c: The message parser.
5 * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
6 * Copyright (C) 1996-2002 Hybrid Development Team
7 * Copyright (C) 2002-2005 ircd-ratbox development team
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
24 * $Id: parse.c 3177 2007-02-01 00:19:14Z jilles $
33 #include "irc_string.h"
34 #include "sprintf_irc.h"
47 * NOTE: parse() should not be called recursively by other functions!
51 /* parv[0] == source, and parv[LAST] == NULL */
52 static char *para
[MAXPARA
+ 2];
54 static void cancel_clients(struct Client
*, struct Client
*, char *);
55 static void remove_unknown(struct Client
*, char *, char *);
57 static void do_numeric(char[], struct Client
*, struct Client
*, int, char **);
58 static void do_alias(struct alias_entry
*, struct Client
*, char *);
60 static int handle_command(struct Message
*, struct Client
*, struct Client
*, int, const char**);
62 static int cmd_hash(const char *p
);
63 static struct Message
*hash_parse(const char *);
64 static struct alias_entry
*alias_parse(const char *);
66 struct MessageHash
*msg_hash_table
[MAX_MSG_HASH
];
68 static char buffer
[1024];
70 dlink_list alias_hash_table
[MAX_MSG_HASH
];
72 /* turn a string into a parc/parv pair */
76 string_to_array(char *string
, char **parv
)
78 char *p
, *buf
= string
;
82 while (*buf
== ' ') /* skip leading spaces */
84 if(*buf
== '\0') /* ignore all-space args */
89 if(*buf
== ':') /* Last parameter */
100 if((p
= strchr(buf
, ' ')) != NULL
)
113 /* we can go upto parv[MAXPARA], as parv[0] is taken by source */
126 * given a raw buffer, parses it and generates parv, parc and sender
129 parse(struct Client
*client_p
, char *pbuffer
, char *bufend
)
131 struct Client
*from
= client_p
;
137 struct Message
*mptr
;
139 s_assert(MyConnect(client_p
));
140 s_assert(client_p
->localClient
->fd
>= 0);
141 if(IsAnyDead(client_p
))
144 for (ch
= pbuffer
; *ch
== ' '; ch
++) /* skip spaces */
145 /* null statement */ ;
147 para
[0] = from
->name
;
153 /* point sender to the sender param */
156 if((s
= strchr(ch
, ' ')))
163 if(*sender
&& IsServer(client_p
))
165 from
= find_client(sender
);
167 /* didnt find any matching client, issue a kill */
170 ServerStats
->is_unpf
++;
171 remove_unknown(client_p
, sender
, pbuffer
);
175 para
[0] = from
->name
;
177 /* fake direction, hmm. */
178 if(from
->from
!= client_p
)
180 ServerStats
->is_wrdi
++;
181 cancel_clients(client_p
, from
, pbuffer
);
191 ServerStats
->is_empt
++;
195 /* at this point there must be some sort of command parameter */
198 * Extract the command code from the packet. Point s to the end
199 * of the command code and calculate the length using pointer
200 * arithmetic. Note: only need length for numerics and *all*
201 * numerics must have parameters and thus a space after the command
205 /* EOB is 3 chars long but is not a numeric */
207 if(*(ch
+ 3) == ' ' && /* ok, lets see if its a possible numeric.. */
208 IsDigit(*ch
) && IsDigit(*(ch
+ 1)) && IsDigit(*(ch
+ 2)))
212 ServerStats
->is_num
++;
213 s
= ch
+ 3; /* I know this is ' ' from above if */
214 *s
++ = '\0'; /* blow away the ' ', and point s to next part */
220 if((s
= strchr(ch
, ' ')))
223 mptr
= hash_parse(ch
);
225 /* no command or its encap only, error */
226 if(!mptr
|| !mptr
->cmd
)
229 * Note: Give error message *only* to recognized
230 * persons. It's a nightmare situation to have
231 * two programs sending "Unknown command"'s or
232 * equivalent to each other at full blast....
233 * If it has got to person state, it at least
234 * seems to be well behaving. Perhaps this message
235 * should never be generated, though... --msa
236 * Hm, when is the buffer empty -- if a command
237 * code has been found ?? -Armin
239 if(pbuffer
[0] != '\0')
241 if (IsPerson(client_p
))
243 struct alias_entry
*aptr
= alias_parse(ch
);
246 do_alias(aptr
, client_p
, s
);
252 sendto_one(from
, form_str(ERR_UNKNOWNCOMMAND
),
253 me
.name
, from
->name
, ch
);
256 ServerStats
->is_unco
++;
260 ii
= bufend
- ((s
) ? s
: ch
);
266 /* XXX this should be done before parse() is called */
273 i
= string_to_array(s
, para
);
277 do_numeric(numeric
, client_p
, from
, i
, para
);
281 if(handle_command(mptr
, client_p
, from
, i
, /* XXX discards const!!! */ (const char **)para
) < -1)
284 for (p
= pbuffer
; p
<= end
; p
+= 8)
287 /* Its expected this nasty code can be removed
288 * or rewritten later if still needed.
290 if((unsigned long) (p
+ 8) > (unsigned long) end
)
292 for (; p
<= end
; p
++)
294 ilog(L_MAIN
, "%02x |%c", p
[0], p
[0]);
299 "%02x %02x %02x %02x %02x %02x %02x %02x |%c%c%c%c%c%c%c%c",
300 p
[0], p
[1], p
[2], p
[3], p
[4], p
[5],
301 p
[6], p
[7], p
[0], p
[1], p
[2], p
[3], p
[4], p
[5], p
[6], p
[7]);
310 * inputs - pointer to message block
311 * - pointer to client
312 * - pointer to client message is from
313 * - count of number of args
314 * - pointer to argv[] array
315 * output - -1 if error from server
319 handle_command(struct Message
*mptr
, struct Client
*client_p
,
320 struct Client
*from
, int i
, const char** hpara
)
322 struct MessageEntry ehandler
;
323 MessageHandler handler
= 0;
324 char squitreason
[80];
326 if(IsAnyDead(client_p
))
329 if(IsServer(client_p
))
334 /* New patch to avoid server flooding from unregistered connects
335 - Pie-Man 07/27/2000 */
337 if(!IsRegistered(client_p
))
339 /* if its from a possible server connection
340 * ignore it.. more than likely its a header thats sneaked through
343 if(IsAnyServer(client_p
) && !(mptr
->flags
& MFLG_UNREG
))
347 ehandler
= mptr
->handlers
[from
->handler
];
348 handler
= ehandler
.handler
;
350 /* check right amount of params is passed... --is */
351 if(i
< ehandler
.min_para
||
352 (ehandler
.min_para
&& EmptyString(hpara
[ehandler
.min_para
- 1])))
354 if(!IsServer(client_p
))
356 sendto_one(client_p
, form_str(ERR_NEEDMOREPARAMS
),
358 EmptyString(client_p
->name
) ? "*" : client_p
->name
,
360 if(MyClient(client_p
))
366 sendto_realops_snomask(SNO_GENERAL
, L_ALL
,
367 "Dropping server %s due to (invalid) command '%s'"
368 " with only %d arguments (expecting %d).",
369 client_p
->name
, mptr
->cmd
, i
, ehandler
.min_para
);
371 "Insufficient parameters (%d < %d) for command '%s' from %s.",
372 i
, ehandler
.min_para
, mptr
->cmd
, client_p
->name
);
373 snprintf(squitreason
, sizeof squitreason
,
374 "Insufficient parameters (%d < %d) for command '%s'",
375 i
, ehandler
.min_para
, mptr
->cmd
);
376 exit_client(client_p
, client_p
, client_p
, squitreason
);
380 (*handler
) (client_p
, from
, i
, hpara
);
385 handle_encap(struct Client
*client_p
, struct Client
*source_p
,
386 const char *command
, int parc
, const char *parv
[])
388 struct Message
*mptr
;
389 struct MessageEntry ehandler
;
390 MessageHandler handler
= 0;
392 parv
[0] = source_p
->name
;
394 mptr
= hash_parse(command
);
396 if(mptr
== NULL
|| mptr
->cmd
== NULL
)
399 ehandler
= mptr
->handlers
[ENCAP_HANDLER
];
400 handler
= ehandler
.handler
;
402 if(parc
< ehandler
.min_para
||
403 (ehandler
.min_para
&& EmptyString(parv
[ehandler
.min_para
- 1])))
406 (*handler
) (client_p
, source_p
, parc
, parv
);
414 * side effects - MUST MUST be called at startup ONCE before
415 * any other keyword hash routine is used.
421 memset(msg_hash_table
, 0, sizeof(msg_hash_table
));
426 * inputs - command name
427 * - pointer to struct Message
429 * side effects - load this one command name
430 * msg->count msg->bytes is modified in place, in
431 * modules address space. Might not want to do that...
434 mod_add_cmd(struct Message
*msg
)
436 struct MessageHash
*ptr
;
437 struct MessageHash
*last_ptr
= NULL
;
438 struct MessageHash
*new_ptr
;
441 s_assert(msg
!= NULL
);
445 msgindex
= cmd_hash(msg
->cmd
);
447 for (ptr
= msg_hash_table
[msgindex
]; ptr
; ptr
= ptr
->next
)
449 if(strcasecmp(msg
->cmd
, ptr
->cmd
) == 0)
450 return; /* Its already added */
454 new_ptr
= (struct MessageHash
*) MyMalloc(sizeof(struct MessageHash
));
456 new_ptr
->next
= NULL
;
457 DupString(new_ptr
->cmd
, msg
->cmd
);
465 msg_hash_table
[msgindex
] = new_ptr
;
467 last_ptr
->next
= new_ptr
;
472 * inputs - command name
474 * side effects - unload this one command name
477 mod_del_cmd(struct Message
*msg
)
479 struct MessageHash
*ptr
;
480 struct MessageHash
*last_ptr
= NULL
;
483 s_assert(msg
!= NULL
);
487 msgindex
= cmd_hash(msg
->cmd
);
489 for (ptr
= msg_hash_table
[msgindex
]; ptr
; ptr
= ptr
->next
)
491 if(strcasecmp(msg
->cmd
, ptr
->cmd
) == 0)
495 last_ptr
->next
= ptr
->next
;
497 msg_hash_table
[msgindex
] = ptr
->next
;
507 * inputs - command name
508 * output - pointer to struct Message
511 static struct Message
*
512 hash_parse(const char *cmd
)
514 struct MessageHash
*ptr
;
517 msgindex
= cmd_hash(cmd
);
519 for (ptr
= msg_hash_table
[msgindex
]; ptr
; ptr
= ptr
->next
)
521 if(strcasecmp(cmd
, ptr
->cmd
) == 0)
530 * inputs - command name
531 * output - pointer to struct Message
534 static struct alias_entry
*
535 alias_parse(const char *cmd
)
540 msgindex
= cmd_hash(cmd
);
542 DLINK_FOREACH(ptr
, alias_hash_table
[msgindex
].head
)
544 struct alias_entry
*ent
= (struct alias_entry
*) ptr
->data
;
546 if(strcasecmp(cmd
, ent
->name
) == 0)
556 * inputs - char string
557 * output - hash index
558 * side effects - NONE
560 * BUGS - This a HORRIBLE hash function
563 cmd_hash(const char *p
)
569 hash_val
+= ((int) (*p
) & 0xDF);
573 return (hash_val
% MAX_MSG_HASH
);
579 * inputs - pointer to client to report to
581 * side effects - NONE
584 report_messages(struct Client
*source_p
)
587 struct MessageHash
*ptr
;
590 for (i
= 0; i
< MAX_MSG_HASH
; i
++)
592 for (ptr
= msg_hash_table
[i
]; ptr
; ptr
= ptr
->next
)
594 s_assert(ptr
->msg
!= NULL
);
595 s_assert(ptr
->cmd
!= NULL
);
597 sendto_one_numeric(source_p
, RPL_STATSCOMMANDS
,
598 form_str(RPL_STATSCOMMANDS
),
599 ptr
->cmd
, ptr
->msg
->count
,
600 ptr
->msg
->bytes
, ptr
->msg
->rcount
);
603 DLINK_FOREACH(pptr
, alias_hash_table
[i
].head
)
605 struct alias_entry
*aptr
= (struct alias_entry
*) pptr
->data
;
607 s_assert(aptr
->name
!= NULL
);
609 sendto_one_numeric(source_p
, RPL_STATSCOMMANDS
,
610 form_str(RPL_STATSCOMMANDS
),
611 aptr
->name
, aptr
->hits
, 0, 0);
618 * inputs - client who sent us the message, client with fake
620 * outputs - a given warning about the fake direction
624 cancel_clients(struct Client
*client_p
, struct Client
*source_p
, char *cmd
)
626 /* ok, fake prefix happens naturally during a burst on a nick
627 * collision with TS5, we cant kill them because one client has to
628 * survive, so we just send an error.
630 if(IsServer(source_p
) || IsMe(source_p
))
632 sendto_realops_snomask(SNO_DEBUG
, L_ALL
,
633 "Message for %s[%s] from %s",
634 source_p
->name
, source_p
->from
->name
,
635 get_server_name(client_p
, SHOW_IP
));
639 sendto_realops_snomask(SNO_DEBUG
, L_ALL
,
640 "Message for %s[%s@%s!%s] from %s (TS, ignored)",
644 source_p
->from
->name
,
645 get_server_name(client_p
, SHOW_IP
));
651 * inputs - client who gave us message, supposed sender, buffer
653 * side effects - kills issued for clients, squits for servers
656 remove_unknown(struct Client
*client_p
, char *lsender
, char *lbuffer
)
658 int slen
= strlen(lsender
);
660 /* meepfoo is a nickname (KILL)
661 * #XXXXXXXX is a UID (KILL)
662 * #XX is a SID (SQUIT)
663 * meep.foo is a server (SQUIT)
665 if((IsDigit(lsender
[0]) && slen
== 3) ||
666 (strchr(lsender
, '.') != NULL
))
668 sendto_realops_snomask(SNO_DEBUG
, L_ALL
,
669 "Unknown prefix (%s) from %s, Squitting %s",
670 lbuffer
, get_server_name(client_p
, SHOW_IP
), lsender
);
673 ":%s SQUIT %s :(Unknown prefix (%s) from %s)",
674 get_id(&me
, client_p
), lsender
,
675 lbuffer
, client_p
->name
);
678 sendto_one(client_p
, ":%s KILL %s :%s (Unknown Client)",
679 get_id(&me
, client_p
), lsender
, me
.name
);
686 * parc number of arguments ('sender' counted as one!)
687 * parv[0] pointer to 'sender' (may point to empty string) (not used)
688 * parv[1]..parv[parc-1]
689 * pointers to additional parameters, this is a NULL
690 * terminated list (parv[parc] == NULL).
693 * Numerics are mostly error reports. If there is something
694 * wrong with the message, just *DROP* it! Don't even think of
695 * sending back a neat error message -- big danger of creating
696 * a ping pong error message...
699 do_numeric(char numeric
[], struct Client
*client_p
, struct Client
*source_p
, int parc
, char *parv
[])
701 struct Client
*target_p
;
702 struct Channel
*chptr
;
704 if(parc
< 2 || !IsServer(source_p
))
707 /* Remap low number numerics. */
708 if(numeric
[0] == '0')
712 * Prepare the parameter portion of the message into 'buffer'.
713 * (Because the buffer is twice as large as the message buffer
714 * for the socket, no overflow can occur here... ...on current
715 * assumptions--bets are off, if these are changed --msa)
716 * Note: if buffer is non-empty, it will begin with SPACE.
720 char *t
= buffer
; /* Current position within the buffer */
722 int tl
; /* current length of presently being built string in t */
723 for (i
= 2; i
< (parc
- 1); i
++)
725 tl
= ircsprintf(t
, " %s", parv
[i
]);
728 ircsprintf(t
, " :%s", parv
[parc
- 1]);
731 if((target_p
= find_client(parv
[1])) != NULL
)
736 * We shouldn't get numerics sent to us,
737 * any numerics we do get indicate a bug somewhere..
739 /* ugh. this is here because of nick collisions. when two servers
740 * relink, they burst each other their nicks, then perform collides.
741 * if there is a nick collision, BOTH servers will kill their own
742 * nicks, and BOTH will kill the other servers nick, which wont exist,
743 * because it will have been already killed by the local server.
745 * unfortunately, as we cant guarantee other servers will do the
746 * "right thing" on a nick collision, we have to keep both kills.
747 * ergo we need to ignore ERR_NOSUCHNICK. --fl_
749 /* quick comment. This _was_ tried. i.e. assume the other servers
750 * will do the "right thing" and kill a nick that is colliding.
751 * unfortunately, it did not work. --Dianora
753 /* note, now we send PING on server connect, we can
754 * also get ERR_NOSUCHSERVER..
756 if(atoi(numeric
) != ERR_NOSUCHNICK
&&
757 atoi(numeric
) != ERR_NOSUCHSERVER
)
758 sendto_realops_snomask(SNO_GENERAL
, L_ADMIN
,
759 "*** %s(via %s) sent a %s numeric to me: %s",
761 client_p
->name
, numeric
, buffer
);
764 else if(target_p
->from
== client_p
)
766 /* This message changed direction (nick collision?)
772 /* csircd will send out unknown umode flag for +a (admin), drop it here. */
773 if((atoi(numeric
) == ERR_UMODEUNKNOWNFLAG
) && MyClient(target_p
))
776 /* Fake it for server hiding, if its our client */
777 sendto_one(target_p
, ":%s %s %s%s",
778 get_id(source_p
, target_p
), numeric
,
779 get_id(target_p
, target_p
), buffer
);
782 else if((chptr
= find_channel(parv
[1])) != NULL
)
783 sendto_channel_local(ALL_MEMBERS
, chptr
,
785 source_p
->name
, numeric
, chptr
->chname
, buffer
);
788 static void do_alias(struct alias_entry
*aptr
, struct Client
*source_p
, char *text
)
791 struct Client
*target_p
;
793 if (!IsFloodDone(source_p
) && source_p
->localClient
->receiveM
> 20)
794 flood_endgrace(source_p
);
796 p
= strchr(aptr
->target
, '@');
800 target_p
= find_server(NULL
, p
+ 1);
801 if (target_p
!= NULL
&& IsMe(target_p
))
806 /* nick, must be +S */
807 target_p
= find_named_person(aptr
->target
);
808 if (target_p
!= NULL
&& !IsService(target_p
))
812 if (target_p
== NULL
)
814 sendto_one_numeric(source_p
, ERR_SERVICESDOWN
, form_str(ERR_SERVICESDOWN
), aptr
->target
);
818 if (text
!= NULL
&& *text
== ':')
820 if (text
== NULL
|| *text
== '\0')
822 sendto_one(source_p
, form_str(ERR_NOTEXTTOSEND
), me
.name
, source_p
->name
);
826 /* increment the hitcounter on this alias */
829 sendto_one(target_p
, ":%s PRIVMSG %s :%s",
830 get_id(source_p
, target_p
),
831 p
!= NULL
? aptr
->target
: get_id(target_p
, target_p
),
836 m_not_oper(struct Client
*client_p
, struct Client
*source_p
, int parc
, const char *parv
[])
838 sendto_one_numeric(source_p
, ERR_NOPRIVILEGES
, form_str(ERR_NOPRIVILEGES
));
843 m_unregistered(struct Client
*client_p
, struct Client
*source_p
, int parc
, const char *parv
[])
846 * I don't =really= want to waste a bit in a flag
847 * number_of_nick_changes is only really valid after the client
848 * is fully registered..
850 if(client_p
->localClient
->number_of_nick_changes
== 0)
852 sendto_one(client_p
, form_str(ERR_NOTREGISTERED
), me
.name
);
853 client_p
->localClient
->number_of_nick_changes
++;
860 m_registered(struct Client
*client_p
, struct Client
*source_p
, int parc
, const char *parv
[])
862 sendto_one(client_p
, form_str(ERR_ALREADYREGISTRED
), me
.name
, source_p
->name
);
867 m_ignore(struct Client
*client_p
, struct Client
*source_p
, int parc
, const char *parv
[])