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.
43 #include "s_numeric.h"
46 #include "querycmds.h"
54 {CLASS_PRIVATE, MSG_PRIVATE, TOK_PRIVATE, m_private, 0, MAXPARA, MFLG_SLOW, 0L},
55 {CLASS_NICK, MSG_NICK, TOK_NICK, m_nick, 0, MAXPARA, MFLG_SLOW|MFLG_UNREG, 0L},
56 {CLASS_NOTICE, MSG_NOTICE, TOK_NOTICE, m_notice, 0, MAXPARA, MFLG_SLOW, 0L},
57 {CLASS_WALLCHOPS, MSG_WALLCHOPS, TOK_WALLCHOPS, m_wallchops, 0, MAXPARA, MFLG_SLOW, 0L},
58 {CLASS_CPRIVMSG, MSG_CPRIVMSG, TOK_CPRIVMSG, m_cprivmsg, 0, MAXPARA, MFLG_SLOW, 0L},
59 {CLASS_CNOTICE, MSG_CNOTICE, TOK_CNOTICE, m_cnotice, 0, MAXPARA, MFLG_SLOW, 0L},
60 {CLASS_JOIN, MSG_JOIN, TOK_JOIN, m_join, 0, MAXPARA, MFLG_SLOW, 0L},
61 {CLASS_MODE, MSG_MODE, TOK_MODE, m_mode, 0, MAXPARA, MFLG_SLOW, 0L},
62 {CLASS_BURST, MSG_BURST, TOK_BURST, m_burst, 0, MAXPARA, MFLG_SLOW, 0L},
63 {CLASS_CREATE, MSG_CREATE, TOK_CREATE, m_create, 0, MAXPARA, MFLG_SLOW, 0L},
64 {CLASS_DESTRUCT, MSG_DESTRUCT, TOK_DESTRUCT, m_destruct, 0, MAXPARA, MFLG_SLOW, 0L},
65 {CLASS_QUIT, MSG_QUIT, TOK_QUIT, m_quit, 0, MAXPARA, MFLG_SLOW|MFLG_UNREG, 0L},
66 {CLASS_PART, MSG_PART, TOK_PART, m_part, 0, MAXPARA, MFLG_SLOW, 0L},
67 {CLASS_TOPIC, MSG_TOPIC, TOK_TOPIC, m_topic, 0, MAXPARA, MFLG_SLOW, 0L},
68 {CLASS_INVITE, MSG_INVITE, TOK_INVITE, m_invite, 0, MAXPARA, MFLG_SLOW, 0L},
69 {CLASS_KICK, MSG_KICK, TOK_KICK, m_kick, 0, MAXPARA, MFLG_SLOW, 0L},
70 {CLASS_WALLOPS, MSG_WALLOPS, TOK_WALLOPS, m_wallops, 0, MAXPARA, MFLG_SLOW, 0L},
71 {CLASS_DESYNCH, MSG_DESYNCH, TOK_DESYNCH, m_desynch, 0, MAXPARA, MFLG_SLOW, 0L},
72 {CLASS_PING, MSG_PING, TOK_PING, m_ping, 0, MAXPARA, MFLG_SLOW, 0L},
73 {CLASS_PONG, MSG_PONG, TOK_PONG, m_pong, 0, MAXPARA, MFLG_SLOW|MFLG_UNREG, 0L},
74 {CLASS_ERROR, MSG_ERROR, TOK_ERROR, m_error, 0, MAXPARA, MFLG_SLOW|MFLG_UNREG, 0L},
75 {CLASS_KILL, MSG_KILL, TOK_KILL, m_kill, 0, MAXPARA, MFLG_SLOW, 0L},
76 {CLASS_USER, MSG_USER, TOK_USER, m_user, 0, MAXPARA, MFLG_SLOW|MFLG_UNREG, 0L},
77 {CLASS_AWAY, MSG_AWAY, TOK_AWAY, m_away, 0, MAXPARA, MFLG_SLOW, 0L},
78 {CLASS_ISON, MSG_ISON, TOK_ISON, m_ison, 0, 1, MFLG_SLOW, 0L},
79 {CLASS_SERVER, MSG_SERVER, TOK_SERVER, m_server, 0, MAXPARA, MFLG_SLOW|MFLG_UNREG, 0L},
80 {CLASS_SQUIT, MSG_SQUIT, TOK_SQUIT, m_squit, 0, MAXPARA, MFLG_SLOW, 0L},
81 {CLASS_WHOIS, MSG_WHOIS, TOK_WHOIS, m_whois, 0, MAXPARA, MFLG_SLOW, 0L},
82 {CLASS_WHO, MSG_WHO, TOK_WHO, m_who, 0, MAXPARA, MFLG_SLOW, 0L},
83 {CLASS_WHOWAS, MSG_WHOWAS, TOK_WHOWAS, m_whowas, 0, MAXPARA, MFLG_SLOW, 0L},
84 {CLASS_LIST, MSG_LIST, TOK_LIST, m_list, 0, MAXPARA, MFLG_SLOW, 0L},
85 {CLASS_NAMES, MSG_NAMES, TOK_NAMES, m_names, 0, MAXPARA, MFLG_SLOW, 0L},
86 {CLASS_USERHOST, MSG_USERHOST, TOK_USERHOST, m_userhost, 0, 1, MFLG_SLOW, 0L},
87 {CLASS_USERIP, MSG_USERIP, TOK_USERIP, m_userip, 0, 1, MFLG_SLOW, 0L},
88 {CLASS_TRACE, MSG_TRACE, TOK_TRACE, m_trace, 0, MAXPARA, MFLG_SLOW, 0L},
89 {CLASS_PASS, MSG_PASS, TOK_PASS, m_pass, 0, MAXPARA, MFLG_SLOW|MFLG_UNREG, 0L},
90 {CLASS_LUSERS, MSG_LUSERS, TOK_LUSERS, m_lusers, 0, MAXPARA, MFLG_SLOW, 0L},
91 {CLASS_TIME, MSG_TIME, TOK_TIME, m_time, 0, MAXPARA, MFLG_SLOW, 0L},
92 {CLASS_SETTIME, MSG_SETTIME, TOK_SETTIME, m_settime, 0, MAXPARA, MFLG_SLOW, 0L},
93 {CLASS_RPING, MSG_RPING, TOK_RPING, m_rping, 0, MAXPARA, MFLG_SLOW, 0L},
94 {CLASS_RPONG, MSG_RPONG, TOK_RPONG, m_rpong, 0, MAXPARA, MFLG_SLOW, 0L},
95 {CLASS_OPER, MSG_OPER, TOK_OPER, m_oper, 0, MAXPARA, MFLG_SLOW, 0L},
96 {CLASS_CONNECT, MSG_CONNECT, TOK_CONNECT, m_connect, 0, MAXPARA, MFLG_SLOW, 0L},
97 {CLASS_UPING, MSG_UPING, TOK_UPING, m_uping, 0, MAXPARA, MFLG_SLOW, 0L},
98 {CLASS_MAP, MSG_MAP, TOK_MAP, m_map, 0, MAXPARA, MFLG_SLOW, 0L},
99 {CLASS_VERSION, MSG_VERSION, TOK_VERSION, m_version, 0, MAXPARA, MFLG_SLOW|MFLG_UNREG, 0L},
100 {CLASS_STATS, MSG_STATS, TOK_STATS, m_stats, 0, MAXPARA, MFLG_SLOW, 0L},
101 {CLASS_LINKS, MSG_LINKS, TOK_LINKS, m_links, 0, MAXPARA, MFLG_SLOW, 0L},
102 {CLASS_ADMIN, MSG_ADMIN, TOK_ADMIN, m_admin, 0, MAXPARA, MFLG_SLOW|MFLG_UNREG, 0L},
103 {CLASS_HELP, MSG_HELP, TOK_HELP, m_help, 0, MAXPARA, MFLG_SLOW, 0L},
104 {CLASS_INFO, MSG_INFO, TOK_INFO, m_info, 0, MAXPARA, MFLG_SLOW, 0L},
105 {CLASS_MOTD, MSG_MOTD, TOK_MOTD, m_motd, 0, MAXPARA, MFLG_SLOW, 0L},
106 {CLASS_CLOSE, MSG_CLOSE, TOK_CLOSE, m_close, 0, MAXPARA, MFLG_SLOW, 0L},
107 {CLASS_SILENCE, MSG_SILENCE, TOK_SILENCE, m_silence, 0, MAXPARA, MFLG_SLOW, 0L},
108 {CLASS_GLINE, MSG_GLINE, TOK_GLINE, m_gline, 0, MAXPARA, MFLG_SLOW, 0L},
109 {CLASS_END_OF_BURST, MSG_END_OF_BURST, TOK_END_OF_BURST, m_end_of_burst, 0, MAXPARA, MFLG_SLOW, 0L},
110 {CLASS_END_OF_BURST_ACK, MSG_END_OF_BURST_ACK, TOK_END_OF_BURST_ACK, m_end_of_burst_ack, 0, MAXPARA, 1, 0L},
111 {CLASS_HASH, MSG_HASH, TOK_HASH, m_hash, 0, MAXPARA, MFLG_SLOW|MFLG_UNREG, 0L},
112 {CLASS_DNS, MSG_DNS, TOK_DNS, m_dns, 0, MAXPARA, MFLG_SLOW, 0L},
113 #if defined(OPER_REHASH) || defined(LOCOP_REHASH)
114 {CLASS_REHASH, MSG_REHASH, TOK_REHASH, m_rehash, 0, MAXPARA, MFLG_SLOW, 0L},
116 #if defined(OPER_RESTART) || defined(LOCOP_RESTART)
117 {CLASS_RESTART, MSG_RESTART, TOK_RESTART, m_restart, 0, MAXPARA, MFLG_SLOW, 0L},
119 #if defined(OPER_DIE) || defined(LOCOP_DIE)
120 {CLASS_DIE, MSG_DIE, TOK_DIE, m_die, 0, MAXPARA, MFLG_SLOW, 0L},
122 {0, (char *)0, (char *)0, (int (*)(aClient *, aClient *, int, char **))0, 0, 0, 0, 0L}
130 static char *para[MAXPARA + 2]; /* leave room for prefix and null */
133 * Message Tree stuff mostly written by orabidoo, with changes by Dianora.
134 * Adapted to Undernet, adding token support, etc by comstud 10/06/97
137 static aMessageTree msg_tree_cmd;
138 static aMessageTree msg_tree_tok;
141 * Guts of making the token tree...
143 static aMessage **do_msg_tree_tok(aMessageTree *mtree, char *prefix,
146 char newprefix[64]; /* Must be longer than every command name */
148 aMessageTree *mtree1;
151 if (!lp || !strncmp((*mptr)->tok, prefix, lp))
153 if (!mptr[1] || (lp && strncmp(mptr[1]->tok, prefix, lp)))
155 /* last command in the message struct or last command in this prefix */
156 mtree->final = (*mptr)->tok + lp;
158 for (c = 0; c < 26; ++c)
159 mtree->pointers[c] = NULL;
162 /* command in this prefix */
163 if (!strCasediff((*mptr)->tok, prefix))
166 mtree->msg = *mptr++;
171 for (c = 'A'; c <= 'Z'; ++c)
173 if ((*mptr)->tok[lp] == c)
175 mtree1 = (aMessageTree *)RunMalloc(sizeof(aMessageTree));
176 mtree1->final = NULL;
177 mtree->pointers[c - 'A'] = mtree1;
178 strcpy(newprefix, prefix);
180 newprefix[lp + 1] = '\0';
181 mptr = do_msg_tree_tok(mtree1, newprefix, mptr);
182 if (!*mptr || strncmp((*mptr)->tok, prefix, lp))
184 for (c2 = c + 1 - 'A'; c2 < 26; ++c2)
185 mtree->pointers[c2] = NULL;
190 mtree->pointers[c - 'A'] = NULL;
194 MyCoreDump; /* This should never happen */
199 * Guts of making the command tree...
201 static aMessage *do_msg_tree_cmd(aMessageTree *mtree, char *prefix,
204 char newprefix[64]; /* Must be longer than every command name */
206 aMessageTree *mtree1;
209 if (!lp || !strncmp(mptr->cmd, prefix, lp))
211 if (!mptr[1].cmd || (lp && strncmp(mptr[1].cmd, prefix, lp)))
213 /* last command in the message struct or last command in this prefix */
214 mtree->final = mptr->cmd + lp;
216 for (c = 0; c < 26; ++c)
217 mtree->pointers[c] = NULL;
220 /* command in this prefix */
221 if (!strCasediff(mptr->cmd, prefix))
229 for (c = 'A'; c <= 'Z'; ++c)
231 if (mptr->cmd[lp] == c)
233 mtree1 = (aMessageTree *)RunMalloc(sizeof(aMessageTree));
234 mtree1->final = NULL;
235 mtree->pointers[c - 'A'] = mtree1;
236 strcpy(newprefix, prefix);
238 newprefix[lp + 1] = '\0';
239 mptr = do_msg_tree_cmd(mtree1, newprefix, mptr);
240 if (!mptr->cmd || strncmp(mptr->cmd, prefix, lp))
242 for (c2 = c + 1 - 'A'; c2 < 26; ++c2)
243 mtree->pointers[c2] = NULL;
248 mtree->pointers[c - 'A'] = NULL;
252 MyCoreDump; /* This should never happen */
256 static int mcmdcmp(const struct Message *m1, const struct Message *m2)
258 return strcmp(m1->cmd, m2->cmd);
261 static int mtokcmp(const struct Message **m1, const struct Message **m2)
263 return strcmp((*m1)->tok, (*m2)->tok);
267 * Sort the command names.
268 * Create table of pointers into msgtab for tokens.
269 * Create trees for ->cmd and ->tok and free the token pointers.
271 void initmsgtree(void)
274 Reg2 aMessage *msg = msgtab;
276 aMessage **msgtab_tok;
279 for (i = 0; msg->cmd; ++i, ++msg)
281 qsort(msgtab, i, sizeof(aMessage),
282 (int (*)(const void *, const void *))mcmdcmp);
283 msgtab_tok = (aMessage **)RunMalloc((i + 1) * sizeof(aMessage *));
284 for (ii = 0; ii < i; ++ii)
285 msgtab_tok[ii] = msgtab + ii;
286 msgtab_tok[i] = NULL; /* Needed by `do_msg_tree_tok' */
287 qsort(msgtab_tok, i, sizeof(aMessage *),
288 (int (*)(const void *, const void *))mtokcmp);
289 msg = do_msg_tree_cmd(&msg_tree_cmd, "", msgtab);
290 msgtok = do_msg_tree_tok(&msg_tree_tok, "", msgtab_tok);
295 * Generic tree parser which works for both commands and tokens.
298 static struct Message *msg_tree_parse(register char *cmd, aMessageTree *root)
300 register aMessageTree *mtree;
301 register unsigned char r = (0xdf & (unsigned char)*cmd) - 'A';
302 if (r > 25 || !(mtree = root->pointers[r]))
306 r = 0xdf & (unsigned char)*++cmd;
307 if (mtree->final && *mtree->final == r)
309 if ((r -= 'A') > 25 || !(mtree = mtree->pointers[r]))
315 * This one is identical to the one above, but it is slower because it
316 * makes sure that `cmd' matches the _full_ command, exactly.
317 * This is to avoid confusion with commands like /quake on clients
318 * that send unknown commands directly to the server.
320 static struct Message *msg_tree_parse_client(register char *cmd,
323 register aMessageTree *mtree;
324 register unsigned char q = (0xdf & (unsigned char)*cmd) - 'A';
325 if (q > 25 || !(mtree = root->pointers[q]))
329 q = 0xdf & (unsigned char)*++cmd;
330 if (mtree->final && !strCasediff(mtree->final, cmd))
332 if ((q -= 'A') > 25 || !(mtree = mtree->pointers[q]))
340 * NOTE: parse_*() should not be called recusively by any other fucntions!
342 int parse_client(aClient *cptr, char *buffer, char *bufend)
344 Reg1 aClient *from = cptr;
346 Reg3 int i, paramcount, noprefix = 0;
349 Debug((DEBUG_DEBUG, "Parsing: %s", buffer));
350 StoreBuffer((buffer, cptr)); /* Store the buffer now, before
351 we start working on it */
356 para[0] = from->name;
357 for (ch = buffer; *ch == ' '; ch++); /* Eat leading spaces */
358 if (*ch == ':') /* Is any client doing this ? */
360 for (++ch; *ch && *ch != ' '; ++ch); /* Ignore sender prefix from client */
362 ch++; /* Advance to command */
369 Debug((DEBUG_NOTICE, "Empty message from host %s:%s",
370 cptr->name, from->name));
374 if ((s = strchr(ch, ' ')))
378 * This is a client/unregistered entity.
379 * Check long command list only.
381 if (!(mptr = msg_tree_parse_client(ch, &msg_tree_cmd)))
384 * Note: Give error message *only* to recognized
385 * persons. It's a nightmare situation to have
386 * two programs sending "Unknown command"'s or
387 * equivalent to each other at full blast....
388 * If it has got to person state, it at least
389 * seems to be well behaving. Perhaps this message
390 * should never be generated, though... --msa
391 * Hm, when is the buffer empty -- if a command
392 * code has been found ?? -Armin
394 if (buffer[0] != '\0')
397 sendto_one(from, ":%s %d %s %s :Unknown command",
398 me.name, ERR_UNKNOWNCOMMAND, from->name, ch);
399 Debug((DEBUG_ERROR, "Unknown (%s) from %s",
400 ch, get_client_name(cptr, TRUE)));
405 LogMessage((cptr, mptr->msgclass));
407 paramcount = mptr->parameters;
408 i = bufend - ((s) ? s : ch);
410 if ((mptr->flags & MFLG_SLOW))
411 cptr->since += (2 + i / 120);
413 * Allow only 1 msg per 2 seconds
414 * (on average) to prevent dumping.
415 * to keep the response rate up,
416 * bursts of up to 5 msgs are allowed
421 * Must the following loop really be so devious? On
422 * surface it splits the message to parameters from
423 * blank spaces. But, if paramcount has been reached,
424 * the rest of the message goes into this last parameter
425 * (about same effect as ":" has...) --msa
428 /* Note initially true: s==NULL || *(s-1) == '\0' !! */
433 if (paramcount > MAXPARA)
434 paramcount = MAXPARA;
438 * Never "FRANCE " again!! ;-) Clean
439 * out *all* blanks.. --msa
449 * The rest is single parameter--can
450 * include blanks also.
458 for (; *s != ' ' && *s; s++);
463 /* The "unregistered command check" was ugly and mildly inefficient.
464 * I fixed it. :) --Shadow
466 if (!IsUser(cptr) && !(mptr->flags & MFLG_UNREG))
468 sendto_one(from, ":%s %d * %s :Register first.",
469 me.name, ERR_NOTREGISTERED, ch);
474 mptr->func == m_private)
476 mptr->func != m_ping && mptr->func != m_pong)
478 from->user->last = now;
480 return (*mptr->func) (cptr, from, i, para);
483 int parse_server(aClient *cptr, char *buffer, char *bufend)
485 Reg1 aClient *from = cptr;
486 Reg2 char *ch = buffer, *s;
487 Reg3 int len, i, numeric = 0, paramcount;
490 Debug((DEBUG_DEBUG, "Parsing: %s", buffer));
491 StoreBuffer((buffer, cptr)); /* Store the buffer now, before
492 * we start working on it. */
495 len = strlen(buffer);
499 char c = buffer[200];
501 sendto_ops("RCV:%-8.8s(%.4d): \"%s...%s\"",
502 cptr->name, len, buffer, &buffer[len - 200]);
506 sendto_ops("RCV:%-8.8s(%.4d): \"%s\"", cptr->name, len, buffer);
513 para[0] = from->name;
516 * A server ALWAYS sends a prefix. When it starts with a ':' it's the
517 * protocol 9 prefix: a nick or a server name. Otherwise it's a numeric
522 /* Let para[0] point to the name of the sender */
524 if (!(ch = strchr(ch, ' ')))
528 /* And let `from' point to its client structure,
529 opps.. a server is _also_ a client --Nem */
530 from = FindClient(para[0]);
533 * If the client corresponding to the
534 * prefix is not found. We must ignore it,
535 * it is simply a lagged message travelling
536 * upstream a SQUIT that removed the client
541 Debug((DEBUG_NOTICE, "Unknown prefix (%s)(%s) from (%s)",
542 para[0], buffer, cptr->name));
547 * However, the only thing that MUST be
548 * allowed to travel upstream against an
549 * squit, is an SQUIT itself (the timestamp
550 * protects us from being used wrong)
554 para[0] = cptr->name;
560 else if (from->from != cptr)
563 Debug((DEBUG_NOTICE, "Fake direction: Message (%s) coming from (%s)",
564 buffer, cptr->name));
568 else if (Protocol(cptr) > 9) /* Well, not ALWAYS, 2.9 can send no prefix */
570 char numeric_prefix[6];
572 for (i = 0; i < 5; ++i)
574 if ('\0' == ch[i] || ' ' == (numeric_prefix[i] = ch[i]))
579 numeric_prefix[i] = '\0';
581 * We got a numeric nick as prefix
582 * 1 or 2 character prefixes are from servers
583 * 3 or 5 chars are from clients
585 if (' ' == ch[1] || ' ' == ch[2])
586 from = FindNServer(numeric_prefix);
588 from = findNUser(numeric_prefix);
594 while (*ch != ' ' && *ch);
597 * If the client corresponding to the
598 * prefix is not found. We must ignore it,
599 * it is simply a lagged message travelling
600 * upstream a SQUIT that removed the client
602 * There turned out to be other reasons that
603 * a prefix is unknown, needing an upstream
604 * KILL. Also, next to an SQUIT we better
605 * allow a KILL to pass too.
613 if (*ch == 'N' && (ch[1] == ' ' || ch[1] == 'I'))
614 /* Only sent a KILL for a nick change */
617 /* Kill the unknown numeric prefix upstream if
618 * it's server still exists: */
619 if ((server = FindNServer(numeric_prefix)) && server->from == cptr)
620 sendto_one(cptr, "%s KILL %s :%s (Unknown numeric nick)",
621 NumServ(&me), numeric_prefix, me.name);
624 * Things that must be allowed to travel
625 * upstream against an squit:
627 if (ch[1] == 'Q' || (*ch == 'D' && ch[1] == ' ') ||
628 (*ch == 'K' && ch[2] == 'L'))
634 /* Let para[0] point to the name of the sender */
635 para[0] = from->name;
637 if (from->from != cptr)
640 Debug((DEBUG_NOTICE, "Fake direction: Message (%s) coming from (%s)",
641 buffer, cptr->name));
651 Debug((DEBUG_NOTICE, "Empty message from host %s:%s",
652 cptr->name, from->name));
657 * Extract the command code from the packet. Point s to the end
658 * of the command code and calculate the length using pointer
659 * arithmetic. Note: only need length for numerics and *all*
660 * numerics must have parameters and thus a space after the command
663 s = strchr(ch, ' '); /* s -> End of the command code */
664 len = (s) ? (s - ch) : 0;
665 if (len == 3 && isDigit(*ch))
667 numeric = (*ch - '0') * 100 + (*(ch + 1) - '0') * 10 + (*(ch + 2) - '0');
668 paramcount = MAXPARA;
670 mptr = NULL; /* Init. to avoid stupid compiler warning :/ */
677 /* Version Receive Send
679 * 2.10.0 Tkn/Long Long
680 * 2.10.10 Tkn/Long Tkn
683 * Clients/unreg servers always receive/
684 * send long commands -record
688 * This is a server. Check the token command list.
689 * -record!jegelhof@cloud9.net
691 mptr = msg_tree_parse(ch, &msg_tree_tok);
693 #if 1 /* for 2.10.0/2.10.10 */
695 * This code supports 2.9 and 2.10.0 sending long commands.
696 * It makes more calls to strCasediff() than the above
697 * so it will be somewhat slower.
700 mptr = msg_tree_parse(ch, &msg_tree_cmd);
706 * Note: Give error message *only* to recognized
707 * persons. It's a nightmare situation to have
708 * two programs sending "Unknown command"'s or
709 * equivalent to each other at full blast....
710 * If it has got to person state, it at least
711 * seems to be well behaving. Perhaps this message
712 * should never be generated, though... --msa
713 * Hm, when is the buffer empty -- if a command
714 * code has been found ?? -Armin
717 if (buffer[0] != '\0')
719 Debug((DEBUG_ERROR, "Unknown (%s) from %s",
720 ch, get_client_name(cptr, TRUE)));
726 LogMessage((cptr, mptr->msgclass));
728 paramcount = mptr->parameters;
729 i = bufend - ((s) ? s : ch);
733 * Must the following loop really be so devious? On
734 * surface it splits the message to parameters from
735 * blank spaces. But, if paramcount has been reached,
736 * the rest of the message goes into this last parameter
737 * (about same effect as ":" has...) --msa
740 /* Note initially true: s==NULL || *(s-1) == '\0' !! */
745 if (paramcount > MAXPARA)
746 paramcount = MAXPARA;
750 * Never "FRANCE " again!! ;-) Clean
751 * out *all* blanks.. --msa
761 * The rest is single parameter--can
762 * include blanks also.
770 for (; *s != ' ' && *s; s++);
775 return (do_numeric(numeric, (*buffer != ':'), cptr, from, i, para));
778 return (*mptr->func) (cptr, from, i, para);