]>
jfr.im git - solanum.git/blob - ircd/parse.c
2 * charybdis: an advanced 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
8 * Copyright (C) 2007 William Pitcock
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
25 * $Id: parse.c 3177 2007-02-01 00:19:14Z jilles $
46 static struct Dictionary
*cmd_dict
= NULL
;
47 struct Dictionary
*alias_dict
= NULL
;
49 /* parv[0] is not used, and parv[LAST] == NULL */
50 static char *para
[MAXPARA
+ 2];
52 static void cancel_clients(struct Client
*, struct Client
*);
53 static void remove_unknown(struct Client
*, char *, char *);
55 static void do_numeric(char[], struct Client
*, struct Client
*, int, char **);
56 static void do_alias(struct alias_entry
*, struct Client
*, char *);
58 static int handle_command(struct Message
*, struct Client
*, struct Client
*, int, const char**);
60 static char buffer
[1024];
62 /* turn a string into a parc/parv pair */
64 char *reconstruct_parv(int parc
, const char *parv
[])
66 static char tmpbuf
[BUFSIZE
]; int i
;
68 rb_strlcpy(tmpbuf
, parv
[0], BUFSIZE
);
69 for (i
= 1; i
< parc
; i
++)
71 rb_strlcat(tmpbuf
, " ", BUFSIZE
);
72 rb_strlcat(tmpbuf
, parv
[i
], BUFSIZE
);
78 string_to_array(char *string
, char **parv
)
80 char *p
, *buf
= string
;
84 while (*buf
== ' ') /* skip leading spaces */
86 if(*buf
== '\0') /* ignore all-space args */
91 if(*buf
== ':') /* Last parameter */
102 if((p
= strchr(buf
, ' ')) != NULL
)
115 /* we can go upto parv[MAXPARA], as parv[0] is skipped */
128 * given a raw buffer, parses it and generates parv and parc
131 parse(struct Client
*client_p
, char *pbuffer
, char *bufend
)
133 struct Client
*from
= client_p
;
140 struct Message
*mptr
;
142 s_assert(MyConnect(client_p
));
143 s_assert(client_p
->localClient
->F
!= NULL
);
144 if(IsAnyDead(client_p
))
147 for (ch
= pbuffer
; *ch
== ' '; ch
++) /* skip spaces */
148 /* null statement */ ;
150 para
[0] = from
->name
;
156 /* point sender to the sender param */
159 if((s
= strchr(ch
, ' ')))
166 if(*sender
&& IsServer(client_p
))
168 from
= find_client(sender
);
170 /* didnt find any matching client, issue a kill */
173 ServerStats
.is_unpf
++;
174 remove_unknown(client_p
, sender
, pbuffer
);
178 para
[0] = from
->name
;
180 /* fake direction, hmm. */
181 if(from
->from
!= client_p
)
183 ServerStats
.is_wrdi
++;
184 cancel_clients(client_p
, from
);
194 ServerStats
.is_empt
++;
198 /* at this point there must be some sort of command parameter */
201 * Extract the command code from the packet. Point s to the end
202 * of the command code and calculate the length using pointer
203 * arithmetic. Note: only need length for numerics and *all*
204 * numerics must have parameters and thus a space after the command
208 /* EOB is 3 chars long but is not a numeric */
210 if(*(ch
+ 3) == ' ' && /* ok, lets see if its a possible numeric.. */
211 IsDigit(*ch
) && IsDigit(*(ch
+ 1)) && IsDigit(*(ch
+ 2)))
215 ServerStats
.is_num
++;
216 s
= ch
+ 3; /* I know this is ' ' from above if */
217 *s
++ = '\0'; /* blow away the ' ', and point s to next part */
223 if((s
= strchr(ch
, ' ')))
226 mptr
= irc_dictionary_retrieve(cmd_dict
, ch
);
228 /* no command or its encap only, error */
229 if(!mptr
|| !mptr
->cmd
)
232 * Note: Give error message *only* to recognized
233 * persons. It's a nightmare situation to have
234 * two programs sending "Unknown command"'s or
235 * equivalent to each other at full blast....
236 * If it has got to person state, it at least
237 * seems to be well behaving. Perhaps this message
238 * should never be generated, though... --msa
239 * Hm, when is the buffer empty -- if a command
240 * code has been found ?? -Armin
242 if(pbuffer
[0] != '\0')
244 if (IsPerson(client_p
))
246 struct alias_entry
*aptr
= irc_dictionary_retrieve(alias_dict
, ch
);
249 do_alias(aptr
, client_p
, s
);
255 sendto_one(from
, form_str(ERR_UNKNOWNCOMMAND
),
256 me
.name
, from
->name
, ch
);
259 ServerStats
.is_unco
++;
263 ii
= bufend
- ((s
) ? s
: ch
);
269 /* XXX this should be done before parse() is called */
276 i
= string_to_array(s
, para
);
280 do_numeric(numeric
, client_p
, from
, i
, para
);
284 if(handle_command(mptr
, client_p
, from
, i
, /* XXX discards const!!! */ (const char **)(void *)para
) < -1)
287 for (p
= pbuffer
; p
<= end
; p
+= 8)
290 /* Its expected this nasty code can be removed
291 * or rewritten later if still needed.
293 if((unsigned long) (p
+ 8) > (unsigned long) end
)
295 for (; p
<= end
; p
++)
297 ilog(L_MAIN
, "%02x |%c", p
[0], p
[0]);
302 "%02x %02x %02x %02x %02x %02x %02x %02x |%c%c%c%c%c%c%c%c",
303 p
[0], p
[1], p
[2], p
[3], p
[4], p
[5],
304 p
[6], p
[7], p
[0], p
[1], p
[2], p
[3], p
[4], p
[5], p
[6], p
[7]);
313 * inputs - pointer to message block
314 * - pointer to client
315 * - pointer to client message is from
316 * - count of number of args
317 * - pointer to argv[] array
318 * output - -1 if error from server
322 handle_command(struct Message
*mptr
, struct Client
*client_p
,
323 struct Client
*from
, int i
, const char** hpara
)
325 struct MessageEntry ehandler
;
326 MessageHandler handler
= 0;
327 char squitreason
[80];
329 if(IsAnyDead(client_p
))
332 if(IsServer(client_p
))
337 /* New patch to avoid server flooding from unregistered connects
338 - Pie-Man 07/27/2000 */
340 if(!IsRegistered(client_p
))
342 /* if its from a possible server connection
343 * ignore it.. more than likely its a header thats sneaked through
346 if(IsAnyServer(client_p
) && !(mptr
->flags
& MFLG_UNREG
))
350 ehandler
= mptr
->handlers
[from
->handler
];
351 handler
= ehandler
.handler
;
353 /* check right amount of params is passed... --is */
354 if(i
< ehandler
.min_para
||
355 (ehandler
.min_para
&& EmptyString(hpara
[ehandler
.min_para
- 1])))
357 if(!IsServer(client_p
))
359 sendto_one(client_p
, form_str(ERR_NEEDMOREPARAMS
),
361 EmptyString(client_p
->name
) ? "*" : client_p
->name
,
363 if(MyClient(client_p
))
369 sendto_realops_snomask(SNO_GENERAL
, L_ALL
,
370 "Dropping server %s due to (invalid) command '%s'"
371 " with only %d arguments (expecting %d).",
372 client_p
->name
, mptr
->cmd
, i
, ehandler
.min_para
);
374 "Insufficient parameters (%d < %d) for command '%s' from %s.",
375 i
, ehandler
.min_para
, mptr
->cmd
, client_p
->name
);
376 rb_snprintf(squitreason
, sizeof squitreason
,
377 "Insufficient parameters (%d < %d) for command '%s'",
378 i
, ehandler
.min_para
, mptr
->cmd
);
379 exit_client(client_p
, client_p
, client_p
, squitreason
);
383 (*handler
) (client_p
, from
, i
, hpara
);
388 handle_encap(struct Client
*client_p
, struct Client
*source_p
,
389 const char *command
, int parc
, const char *parv
[])
391 struct Message
*mptr
;
392 struct MessageEntry ehandler
;
393 MessageHandler handler
= 0;
395 mptr
= irc_dictionary_retrieve(cmd_dict
, command
);
397 if(mptr
== NULL
|| mptr
->cmd
== NULL
)
400 ehandler
= mptr
->handlers
[ENCAP_HANDLER
];
401 handler
= ehandler
.handler
;
403 if(parc
< ehandler
.min_para
||
404 (ehandler
.min_para
&& EmptyString(parv
[ehandler
.min_para
- 1])))
407 (*handler
) (client_p
, source_p
, parc
, parv
);
415 * side effects - MUST MUST be called at startup ONCE before
416 * any other keyword hash routine is used.
422 cmd_dict
= irc_dictionary_create("command", strcasecmp
);
427 * inputs - command name
428 * - pointer to struct Message
430 * side effects - load this one command name
431 * msg->count msg->bytes is modified in place, in
432 * modules address space. Might not want to do that...
435 mod_add_cmd(struct Message
*msg
)
437 s_assert(msg
!= NULL
);
441 if (irc_dictionary_find(cmd_dict
, msg
->cmd
) != NULL
)
448 irc_dictionary_add(cmd_dict
, msg
->cmd
, msg
);
453 * inputs - command name
455 * side effects - unload this one command name
458 mod_del_cmd(struct Message
*msg
)
460 s_assert(msg
!= NULL
);
464 irc_dictionary_delete(cmd_dict
, msg
->cmd
);
470 * inputs - pointer to client to report to
472 * side effects - NONE
475 report_messages(struct Client
*source_p
)
477 struct DictionaryIter iter
;
479 struct alias_entry
*amsg
;
481 DICTIONARY_FOREACH(msg
, &iter
, cmd_dict
)
483 s_assert(msg
->cmd
!= NULL
);
484 sendto_one_numeric(source_p
, RPL_STATSCOMMANDS
,
485 form_str(RPL_STATSCOMMANDS
),
486 msg
->cmd
, msg
->count
,
487 msg
->bytes
, msg
->rcount
);
490 DICTIONARY_FOREACH(amsg
, &iter
, alias_dict
)
492 s_assert(amsg
->name
!= NULL
);
493 sendto_one_numeric(source_p
, RPL_STATSCOMMANDS
,
494 form_str(RPL_STATSCOMMANDS
),
495 amsg
->name
, amsg
->hits
, 0L, 0);
501 * inputs - client who sent us the message, client with fake
503 * outputs - a given warning about the fake direction
507 cancel_clients(struct Client
*client_p
, struct Client
*source_p
)
509 /* ok, fake prefix happens naturally during a burst on a nick
510 * collision with TS5, we cant kill them because one client has to
511 * survive, so we just send an error.
513 if(IsServer(source_p
) || IsMe(source_p
))
515 sendto_realops_snomask(SNO_DEBUG
, L_ALL
,
516 "Message for %s[%s] from %s",
517 source_p
->name
, source_p
->from
->name
,
522 sendto_realops_snomask(SNO_DEBUG
, L_ALL
,
523 "Message for %s[%s@%s!%s] from %s (TS, ignored)",
527 source_p
->from
->name
,
534 * inputs - client who gave us message, supposed sender, buffer
536 * side effects - kills issued for clients, squits for servers
539 remove_unknown(struct Client
*client_p
, char *lsender
, char *lbuffer
)
541 int slen
= strlen(lsender
);
543 struct Client
*server
;
545 /* meepfoo is a nickname (ignore)
546 * #XXXXXXXX is a UID (KILL)
547 * #XX is a SID (SQUIT)
548 * meep.foo is a server (SQUIT)
550 if((IsDigit(lsender
[0]) && slen
== 3) ||
551 (strchr(lsender
, '.') != NULL
))
553 sendto_realops_snomask(SNO_DEBUG
, L_ALL
,
554 "Unknown prefix (%s) from %s, Squitting %s",
555 lbuffer
, client_p
->name
, lsender
);
558 ":%s SQUIT %s :(Unknown prefix (%s) from %s)",
559 get_id(&me
, client_p
), lsender
,
560 lbuffer
, client_p
->name
);
562 else if(!IsDigit(lsender
[0]))
565 sendto_realops_snomask(SNO_DEBUG
, L_ALL
,
566 "Invalid prefix (%s) from %s",
567 lbuffer
, client_p
->name
);
570 memcpy(sid
, lsender
, 3);
572 server
= find_server(NULL
, sid
);
573 if (server
!= NULL
&& server
->from
== client_p
)
574 sendto_one(client_p
, ":%s KILL %s :%s (Unknown Client)",
575 get_id(&me
, client_p
), lsender
, me
.name
);
583 * parc number of arguments ('sender' counted as one!)
584 * parv[1]..parv[parc-1]
585 * pointers to additional parameters, this is a NULL
586 * terminated list (parv[parc] == NULL).
589 * Numerics are mostly error reports. If there is something
590 * wrong with the message, just *DROP* it! Don't even think of
591 * sending back a neat error message -- big danger of creating
592 * a ping pong error message...
595 do_numeric(char numeric
[], struct Client
*client_p
, struct Client
*source_p
, int parc
, char *parv
[])
597 struct Client
*target_p
;
598 struct Channel
*chptr
;
600 if(parc
< 2 || !IsServer(source_p
))
603 /* Remap low number numerics. */
604 if(numeric
[0] == '0')
608 * Prepare the parameter portion of the message into 'buffer'.
609 * (Because the buffer is twice as large as the message buffer
610 * for the socket, no overflow can occur here... ...on current
611 * assumptions--bets are off, if these are changed --msa)
612 * Note: if buffer is non-empty, it will begin with SPACE.
616 char *t
= buffer
; /* Current position within the buffer */
618 int tl
; /* current length of presently being built string in t */
619 for (i
= 2; i
< (parc
- 1); i
++)
621 tl
= rb_sprintf(t
, " %s", parv
[i
]);
624 rb_sprintf(t
, " :%s", parv
[parc
- 1]);
627 if((target_p
= find_client(parv
[1])) != NULL
)
632 * We shouldn't get numerics sent to us,
633 * any numerics we do get indicate a bug somewhere..
635 /* ugh. this is here because of nick collisions. when two servers
636 * relink, they burst each other their nicks, then perform collides.
637 * if there is a nick collision, BOTH servers will kill their own
638 * nicks, and BOTH will kill the other servers nick, which wont exist,
639 * because it will have been already killed by the local server.
641 * unfortunately, as we cant guarantee other servers will do the
642 * "right thing" on a nick collision, we have to keep both kills.
643 * ergo we need to ignore ERR_NOSUCHNICK. --fl_
645 /* quick comment. This _was_ tried. i.e. assume the other servers
646 * will do the "right thing" and kill a nick that is colliding.
647 * unfortunately, it did not work. --Dianora
649 /* note, now we send PING on server connect, we can
650 * also get ERR_NOSUCHSERVER..
652 if(atoi(numeric
) != ERR_NOSUCHNICK
&&
653 atoi(numeric
) != ERR_NOSUCHSERVER
)
654 sendto_realops_snomask(SNO_GENERAL
, L_ADMIN
,
655 "*** %s(via %s) sent a %s numeric to me: %s",
657 client_p
->name
, numeric
, buffer
);
660 else if(target_p
->from
== client_p
)
662 /* This message changed direction (nick collision?)
668 /* csircd will send out unknown umode flag for +a (admin), drop it here. */
669 if((atoi(numeric
) == ERR_UMODEUNKNOWNFLAG
) && MyClient(target_p
))
672 /* Fake it for server hiding, if its our client */
673 sendto_one(target_p
, ":%s %s %s%s",
674 get_id(source_p
, target_p
), numeric
,
675 get_id(target_p
, target_p
), buffer
);
678 else if((chptr
= find_channel(parv
[1])) != NULL
)
679 sendto_channel_flags(client_p
, ALL_MEMBERS
, source_p
, chptr
,
681 numeric
, chptr
->chname
, buffer
);
684 static void do_alias(struct alias_entry
*aptr
, struct Client
*source_p
, char *text
)
687 struct Client
*target_p
;
689 if (!IsFloodDone(source_p
) && source_p
->localClient
->receiveM
> 20)
690 flood_endgrace(source_p
);
692 p
= strchr(aptr
->target
, '@');
696 target_p
= find_server(NULL
, p
+ 1);
697 if (target_p
!= NULL
&& IsMe(target_p
))
702 /* nick, must be +S */
703 target_p
= find_named_person(aptr
->target
);
704 if (target_p
!= NULL
&& !IsService(target_p
))
708 if (target_p
== NULL
)
710 sendto_one_numeric(source_p
, ERR_SERVICESDOWN
, form_str(ERR_SERVICESDOWN
), aptr
->target
);
714 if (text
!= NULL
&& *text
== ':')
716 if (text
== NULL
|| *text
== '\0')
718 sendto_one(source_p
, form_str(ERR_NOTEXTTOSEND
), me
.name
, source_p
->name
);
722 /* increment the hitcounter on this alias */
725 sendto_one(target_p
, ":%s PRIVMSG %s :%s",
726 get_id(source_p
, target_p
),
727 p
!= NULL
? aptr
->target
: get_id(target_p
, target_p
),
732 m_not_oper(struct Client
*client_p
, struct Client
*source_p
, int parc
, const char *parv
[])
734 sendto_one_numeric(source_p
, ERR_NOPRIVILEGES
, form_str(ERR_NOPRIVILEGES
));
739 m_unregistered(struct Client
*client_p
, struct Client
*source_p
, int parc
, const char *parv
[])
742 * I don't =really= want to waste a bit in a flag
743 * number_of_nick_changes is only really valid after the client
744 * is fully registered..
746 if(client_p
->localClient
->number_of_nick_changes
== 0)
748 sendto_one(client_p
, form_str(ERR_NOTREGISTERED
), me
.name
);
749 client_p
->localClient
->number_of_nick_changes
++;
756 m_registered(struct Client
*client_p
, struct Client
*source_p
, int parc
, const char *parv
[])
758 sendto_one(client_p
, form_str(ERR_ALREADYREGISTRED
), me
.name
, source_p
->name
);
763 m_ignore(struct Client
*client_p
, struct Client
*source_p
, int parc
, const char *parv
[])