ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/parse.c
Revision: 2916
Committed: Sat Jan 25 21:09:18 2014 UTC (11 years, 7 months ago) by michael
Content type: text/x-csrc
File size: 22434 byte(s)
Log Message:
- Clean up all files in include/ (fixed indentation, removed whitespaces/tabs)
- Fixed copyright years

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 *
4 * Copyright (c) 1997-2014 ircd-hybrid development team
5 *
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 2 of the License, or
9 * (at your option) any later version.
10 *
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.
15 *
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 * USA
20 */
21
22 /*! \file parse.c
23 * \brief The message parser.
24 * \version $Id$
25 */
26
27 #include "stdinc.h"
28 #include "client.h"
29 #include "parse.h"
30 #include "channel.h"
31 #include "hash.h"
32 #include "irc_string.h"
33 #include "ircd.h"
34 #include "numeric.h"
35 #include "log.h"
36 #include "send.h"
37 #include "conf.h"
38 #include "memory.h"
39 #include "s_user.h"
40 #include "s_serv.h"
41
42
43 /*
44 * (based on orabidoo's parser code)
45 *
46 * This has always just been a trie. Look at volume III of Knuth ACP
47 *
48 *
49 * ok, you start out with an array of pointers, each one corresponds
50 * to a letter at the current position in the command being examined.
51 *
52 * so roughly you have this for matching 'trie' or 'tie'
53 *
54 * 't' points -> [MessageTree *] 'r' -> [MessageTree *] -> 'i'
55 * -> [MessageTree *] -> [MessageTree *] -> 'e' and matches
56 *
57 * 'i' -> [MessageTree *] -> 'e' and matches
58 *
59 * BUGS (Limitations!)
60 *
61 * I designed this trie to parse ircd commands. Hence it currently
62 * casefolds. This is trivial to fix by increasing MAXPTRLEN.
63 * This trie also "folds" '{' etc. down. This means, the input to this
64 * trie must be alpha tokens only. This again, is a limitation that
65 * can be overcome by increasing MAXPTRLEN to include upper/lower case
66 * at the expense of more memory. At the extreme end, you could make
67 * MAXPTRLEN 128.
68 *
69 * This is also not a patricia trie. On short ircd tokens, this is
70 * not likely going to matter.
71 *
72 * Diane Bruce (Dianora), June 6 2003
73 */
74
75 #define MAXPTRLEN 32
76 /* Must be a power of 2, and
77 * larger than 26 [a-z]|[A-Z]
78 * its used to allocate the set
79 * of pointers at each node of the tree
80 * There are MAXPTRLEN pointers at each node.
81 * Obviously, there have to be more pointers
82 * Than ASCII letters. 32 is a nice number
83 * since there is then no need to shift
84 * 'A'/'a' to base 0 index, at the expense
85 * of a few never used pointers. For a small
86 * parser like this, this is a good compromise
87 * and does make it somewhat faster.
88 *
89 * - Dianora
90 */
91
92 struct MessageTree
93 {
94 int links; /* Count of all pointers (including msg) at this node
95 * used as reference count for deletion of _this_ node.
96 */
97 struct Message *msg;
98 struct MessageTree *pointers[MAXPTRLEN];
99 };
100
101 static struct MessageTree msg_tree;
102
103 /*
104 * NOTE: parse() should not be called recursively by other functions!
105 */
106 static char *sender;
107 static char *para[MAXPARA + 2]; /* <prefix> + <params> + NULL */
108
109 static int cancel_clients(struct Client *, struct Client *, char *);
110 static void remove_unknown(struct Client *, char *, char *);
111 static void handle_numeric(char[], struct Client *, struct Client *, int, char *[]);
112 static void handle_command(struct Message *, struct Client *, struct Client *, unsigned int, char *[]);
113
114
115 /*
116 * parse a buffer.
117 *
118 * NOTE: parse() should not be called recusively by any other functions!
119 */
120 void
121 parse(struct Client *client_p, char *pbuffer, char *bufend)
122 {
123 struct Client *from = client_p;
124 struct Message *msg_ptr = NULL;
125 char *ch = NULL;
126 char *s = NULL;
127 char *numeric = NULL;
128 unsigned int parc = 0;
129 unsigned int paramcount;
130
131 if (IsDefunct(client_p))
132 return;
133
134 assert(client_p->localClient->fd.flags.open);
135 assert((bufend - pbuffer) < IRCD_BUFSIZE);
136
137 for (ch = pbuffer; *ch == ' '; ++ch) /* skip spaces */
138 /* null statement */ ;
139
140 if (*ch == ':')
141 {
142 /*
143 * Copy the prefix to 'sender' assuming it terminates
144 * with SPACE (or NULL, which is an error, though).
145 */
146 sender = ++ch;
147
148 if ((s = strchr(ch, ' ')) != NULL)
149 {
150 *s = '\0';
151 ch = ++s;
152 }
153
154 if (*sender && IsServer(client_p))
155 {
156 if ((from = find_person(client_p, sender)) == NULL)
157 from = hash_find_server(sender);
158
159 /*
160 * Hmm! If the client corresponding to the prefix is not found--what is
161 * the correct action??? Now, I will ignore the message (old IRC just
162 * let it through as if the prefix just wasn't there...) --msa
163 */
164 if (from == NULL)
165 {
166 ++ServerStats.is_unpf;
167 remove_unknown(client_p, sender, pbuffer);
168 return;
169 }
170
171 if (from->from != client_p)
172 {
173 ++ServerStats.is_wrdi;
174 cancel_clients(client_p, from, pbuffer);
175 return;
176 }
177 }
178
179 while (*ch == ' ')
180 ++ch;
181 }
182
183 if (*ch == '\0')
184 {
185 ++ServerStats.is_empt;
186 return;
187 }
188
189 /*
190 * Extract the command code from the packet. Point s to the end
191 * of the command code and calculate the length using pointer
192 * arithmetic. Note: only need length for numerics and *all*
193 * numerics must have parameters and thus a space after the command
194 * code. -avalon
195 */
196
197 /* EOB is 3 chars long but is not a numeric */
198 if (*(ch + 3) == ' ' && /* ok, lets see if its a possible numeric.. */
199 IsDigit(*ch) && IsDigit(*(ch + 1)) && IsDigit(*(ch + 2)))
200 {
201 numeric = ch;
202 paramcount = 2; /* destination, and the rest of it */
203 ++ServerStats.is_num;
204 s = ch + 3; /* I know this is ' ' from above if */
205 *s++ = '\0'; /* blow away the ' ', and point s to next part */
206 }
207 else
208 {
209 unsigned int ii = 0;
210
211 if ((s = strchr(ch, ' ')) != NULL)
212 *s++ = '\0';
213
214 if ((msg_ptr = find_command(ch)) == NULL)
215 {
216 /*
217 * Note: Give error message *only* to recognized
218 * persons. It's a nightmare situation to have
219 * two programs sending "Unknown command"'s or
220 * equivalent to each other at full blast....
221 * If it has got to person state, it at least
222 * seems to be well behaving. Perhaps this message
223 * should never be generated, though... --msa
224 * Hm, when is the buffer empty -- if a command
225 * code has been found ?? -Armin
226 */
227 if (*pbuffer != '\0')
228 {
229 if (IsClient(from))
230 sendto_one(from, form_str(ERR_UNKNOWNCOMMAND),
231 me.name, from->name, ch);
232 }
233
234 ++ServerStats.is_unco;
235 return;
236 }
237
238 assert(msg_ptr->cmd != NULL);
239
240 paramcount = msg_ptr->args_max;
241 ii = bufend - ((s) ? s : ch);
242 msg_ptr->bytes += ii;
243 }
244
245 /*
246 * Must the following loop really be so devious? On surface it
247 * splits the message to parameters from blank spaces. But, if
248 * paramcount has been reached, the rest of the message goes into
249 * this last parameter (about same effect as ":" has...) --msa
250 */
251
252 /* Note initially true: s==NULL || *(s-1) == '\0' !! */
253
254 para[parc] = from->name;
255
256 if (s)
257 {
258 if (paramcount > MAXPARA)
259 paramcount = MAXPARA;
260
261 while (1)
262 {
263 while (*s == ' ')
264 *s++ = '\0';
265
266 if (*s == '\0')
267 break;
268
269 if (*s == ':')
270 {
271 /* The rest is a single parameter */
272 para[++parc] = s + (!numeric); /* keep the colon if it's a numeric */
273 break;
274 }
275
276 para[++parc] = s;
277
278 if (parc >= paramcount)
279 break;
280
281 while (*s && *s != ' ')
282 ++s;
283 }
284 }
285
286 para[++parc] = NULL;
287
288 if (msg_ptr != NULL)
289 handle_command(msg_ptr, client_p, from, parc, para);
290 else
291 handle_numeric(numeric, client_p, from, parc, para);
292 }
293
294 /* handle_command()
295 *
296 * inputs - pointer to message block
297 * - pointer to client
298 * - pointer to client message is from
299 * - count of number of args
300 * - pointer to argv[] array
301 * output - -1 if error from server
302 * side effects -
303 */
304 static void
305 handle_command(struct Message *mptr, struct Client *client_p,
306 struct Client *from, unsigned int i, char *hpara[])
307 {
308 MessageHandler handler = 0;
309
310 if (IsServer(client_p))
311 mptr->rcount++;
312
313 mptr->count++;
314
315 handler = mptr->handlers[client_p->handler];
316
317 /* check right amount of params is passed... --is */
318 if (i < mptr->args_min)
319 {
320 if (!IsServer(client_p))
321 {
322 sendto_one(client_p, form_str(ERR_NEEDMOREPARAMS), me.name,
323 EmptyString(hpara[0]) ? "*" : hpara[0], mptr->cmd);
324 }
325 else
326 {
327 sendto_realops_flags(UMODE_ALL, L_ALL, SEND_NOTICE,
328 "Dropping server %s due to (invalid) command '%s' "
329 "with only %d arguments (expecting %d).",
330 client_p->name, mptr->cmd, i, mptr->args_min);
331 ilog(LOG_TYPE_IRCD, "Insufficient parameters (%d) for command '%s' from %s.",
332 i, mptr->cmd, client_p->name);
333 exit_client(client_p, client_p,
334 "Not enough arguments to server command.");
335 }
336 }
337 else
338 (*handler)(client_p, from, i, hpara);
339 }
340
341 /* add_msg_element()
342 *
343 * inputs - pointer to MessageTree
344 * - pointer to Message to add for given command
345 * - pointer to current portion of command being added
346 * output - NONE
347 * side effects - recursively build the Message Tree ;-)
348 */
349 /*
350 * How this works.
351 *
352 * The code first checks to see if its reached the end of the command
353 * If so, that struct MessageTree has a msg pointer updated and the links
354 * count incremented, since a msg pointer is a reference.
355 * Then the code descends recursively, building the trie.
356 * If a pointer index inside the struct MessageTree is NULL a new
357 * child struct MessageTree has to be allocated.
358 * The links (reference count) is incremented as they are created
359 * in the parent.
360 */
361 static void
362 add_msg_element(struct MessageTree *mtree_p, struct Message *msg_p,
363 const char *cmd)
364 {
365 struct MessageTree *ntree_p;
366
367 if (*cmd == '\0')
368 {
369 mtree_p->msg = msg_p;
370 mtree_p->links++; /* Have msg pointer, so up ref count */
371 }
372 else
373 {
374 /*
375 * *cmd & (MAXPTRLEN-1)
376 * convert the char pointed to at *cmd from ASCII to an integer
377 * between 0 and MAXPTRLEN.
378 * Thus 'A' -> 0x1 'B' -> 0x2 'c' -> 0x3 etc.
379 */
380 if ((ntree_p = mtree_p->pointers[*cmd & (MAXPTRLEN - 1)]) == NULL)
381 {
382 ntree_p = MyMalloc(sizeof(struct MessageTree));
383 mtree_p->pointers[*cmd & (MAXPTRLEN - 1)] = ntree_p;
384
385 mtree_p->links++; /* Have new pointer, so up ref count */
386 }
387
388 add_msg_element(ntree_p, msg_p, cmd + 1);
389 }
390 }
391
392 /* del_msg_element()
393 *
394 * inputs - Pointer to MessageTree to delete from
395 * - pointer to command name to delete
396 * output - NONE
397 * side effects - recursively deletes a token from the Message Tree ;-)
398 */
399 /*
400 * How this works.
401 *
402 * Well, first off, the code recursively descends into the trie
403 * until it finds the terminating letter of the command being removed.
404 * Once it has done that, it marks the msg pointer as NULL then
405 * reduces the reference count on that allocated struct MessageTree
406 * since a command counts as a reference.
407 *
408 * Then it pops up the recurse stack. As it comes back up the recurse
409 * The code checks to see if the child now has no pointers or msg
410 * i.e. the links count has gone to zero. If its no longer used, the
411 * child struct MessageTree can be deleted. The parent reference
412 * to this child is then removed and the parents link count goes down.
413 * Thus, we continue to go back up removing all unused MessageTree(s)
414 */
415 static void
416 del_msg_element(struct MessageTree *mtree_p, const char *cmd)
417 {
418 struct MessageTree *ntree_p;
419
420 /*
421 * In case this is called for a nonexistent command
422 * check that there is a msg pointer here, else links-- goes -ve
423 * -db
424 */
425 if ((*cmd == '\0') && (mtree_p->msg != NULL))
426 {
427 mtree_p->msg = NULL;
428 mtree_p->links--;
429 }
430 else
431 {
432 if ((ntree_p = mtree_p->pointers[*cmd & (MAXPTRLEN - 1)]) != NULL)
433 {
434 del_msg_element(ntree_p, cmd + 1);
435
436 if (ntree_p->links == 0)
437 {
438 mtree_p->pointers[*cmd & (MAXPTRLEN - 1)] = NULL;
439 mtree_p->links--;
440 MyFree(ntree_p);
441 }
442 }
443 }
444 }
445
446 /* msg_tree_parse()
447 *
448 * inputs - Pointer to command to find
449 * - Pointer to MessageTree root
450 * output - Find given command returning Message * if found NULL if not
451 * side effects - none
452 */
453 static struct Message *
454 msg_tree_parse(const char *cmd)
455 {
456 struct MessageTree *mtree = &msg_tree;
457 assert(cmd && *cmd);
458
459 while (IsAlpha(*cmd) && (mtree = mtree->pointers[*cmd & (MAXPTRLEN - 1)]))
460 if (*++cmd == '\0')
461 return mtree->msg;
462
463 return NULL;
464 }
465
466 /* mod_add_cmd()
467 *
468 * inputs - pointer to struct Message
469 * output - none
470 * side effects - load this one command name
471 * msg->count msg->bytes is modified in place, in
472 * modules address space. Might not want to do that...
473 */
474 void
475 mod_add_cmd(struct Message *msg)
476 {
477 assert(msg && msg->cmd);
478
479 /* command already added? */
480 if (msg_tree_parse(msg->cmd))
481 return;
482
483 add_msg_element(&msg_tree, msg, msg->cmd);
484 msg->count = msg->rcount = msg->bytes = 0;
485 }
486
487 /* mod_del_cmd()
488 *
489 * inputs - pointer to struct Message
490 * output - none
491 * side effects - unload this one command name
492 */
493 void
494 mod_del_cmd(struct Message *msg)
495 {
496 assert(msg && msg->cmd);
497
498 del_msg_element(&msg_tree, msg->cmd);
499 }
500
501 /* find_command()
502 *
503 * inputs - command name
504 * output - pointer to struct Message
505 * side effects - none
506 */
507 struct Message *
508 find_command(const char *cmd)
509 {
510 return msg_tree_parse(cmd);
511 }
512
513 static void
514 recurse_report_messages(struct Client *source_p, const struct MessageTree *mtree)
515 {
516 unsigned int i;
517
518 if (mtree->msg != NULL)
519 sendto_one(source_p, form_str(RPL_STATSCOMMANDS),
520 me.name, source_p->name, mtree->msg->cmd,
521 mtree->msg->count, mtree->msg->bytes,
522 mtree->msg->rcount);
523
524 for (i = 0; i < MAXPTRLEN; ++i)
525 if (mtree->pointers[i] != NULL)
526 recurse_report_messages(source_p, mtree->pointers[i]);
527 }
528
529 /* report_messages()
530 *
531 * inputs - pointer to client to report to
532 * output - NONE
533 * side effects - client is shown list of commands
534 */
535 void
536 report_messages(struct Client *source_p)
537 {
538 const struct MessageTree *mtree = &msg_tree;
539 unsigned int i;
540
541 for (i = 0; i < MAXPTRLEN; ++i)
542 if (mtree->pointers[i] != NULL)
543 recurse_report_messages(source_p, mtree->pointers[i]);
544 }
545
546 /* cancel_clients()
547 *
548 * inputs -
549 * output -
550 * side effects -
551 */
552 static int
553 cancel_clients(struct Client *client_p, struct Client *source_p, char *cmd)
554 {
555 /*
556 * Kill all possible points that are causing confusion here,
557 * I'm not sure I've got this all right...
558 * - avalon
559 *
560 * Knowing avalon, probably not.
561 */
562
563 /*
564 * With TS, fake prefixes are a common thing, during the
565 * connect burst when there's a nick collision, and they
566 * must be ignored rather than killed because one of the
567 * two is surviving.. so we don't bother sending them to
568 * all ops everytime, as this could send 'private' stuff
569 * from lagged clients. we do send the ones that cause
570 * servers to be dropped though, as well as the ones from
571 * non-TS servers -orabidoo
572 */
573 /*
574 * Incorrect prefix for a server from some connection. If it is a
575 * client trying to be annoying, just QUIT them, if it is a server
576 * then the same deal.
577 */
578 if (IsServer(source_p) || IsMe(source_p))
579 {
580 sendto_realops_flags(UMODE_DEBUG, L_ADMIN, SEND_NOTICE,
581 "Message for %s[%s] from %s",
582 source_p->name, source_p->from->name,
583 get_client_name(client_p, SHOW_IP));
584 sendto_realops_flags(UMODE_DEBUG, L_OPER, SEND_NOTICE,
585 "Message for %s[%s] from %s",
586 source_p->name, source_p->from->name,
587 get_client_name(client_p, MASK_IP));
588 sendto_realops_flags(UMODE_DEBUG, L_ALL, SEND_NOTICE,
589 "Not dropping server %s (%s) for Fake Direction",
590 client_p->name, source_p->name);
591 return -1;
592 /* return exit_client(client_p, client_p, &me, "Fake Direction");*/
593 }
594
595 /*
596 * Ok, someone is trying to impose as a client and things are
597 * confused. If we got the wrong prefix from a server, send out a
598 * kill, else just exit the lame client.
599 */
600 /*
601 * If the fake prefix is coming from a TS server, discard it
602 * silently -orabidoo
603 *
604 * all servers must be TS these days --is
605 */
606 sendto_realops_flags(UMODE_DEBUG, L_ADMIN, SEND_NOTICE,
607 "Message for %s[%s@%s!%s] from %s (TS, ignored)",
608 source_p->name, source_p->username, source_p->host,
609 source_p->from->name, get_client_name(client_p, SHOW_IP));
610 sendto_realops_flags(UMODE_DEBUG, L_OPER, SEND_NOTICE,
611 "Message for %s[%s@%s!%s] from %s (TS, ignored)",
612 source_p->name, source_p->username, source_p->host,
613 source_p->from->name, get_client_name(client_p, MASK_IP));
614
615 return 0;
616 }
617
618 /* remove_unknown()
619 *
620 * inputs -
621 * output -
622 * side effects -
623 */
624 static void
625 remove_unknown(struct Client *client_p, char *lsender, char *lbuffer)
626 {
627 /*
628 * Do kill if it came from a server because it means there is a ghost
629 * user on the other server which needs to be removed. -avalon
630 * Tell opers about this. -Taner
631 */
632 /*
633 * '[0-9]something' is an ID (KILL/SQUIT depending on its length)
634 * 'nodots' is a nickname (KILL)
635 * 'no.dot.at.start' is a server (SQUIT)
636 */
637 if ((IsDigit(*lsender) && strlen(lsender) <= IRC_MAXSID) ||
638 strchr(lsender, '.') != NULL)
639 {
640 sendto_realops_flags(UMODE_DEBUG, L_ADMIN, SEND_NOTICE,
641 "Unknown prefix (%s) from %s, Squitting %s",
642 lbuffer, get_client_name(client_p, SHOW_IP), lsender);
643 sendto_realops_flags(UMODE_DEBUG, L_OPER, SEND_NOTICE,
644 "Unknown prefix (%s) from %s, Squitting %s",
645 lbuffer, client_p->name, lsender);
646 sendto_one(client_p, ":%s SQUIT %s :(Unknown prefix (%s) from %s)",
647 me.name, lsender, lbuffer, client_p->name);
648 }
649 else
650 sendto_one(client_p, ":%s KILL %s :%s (Unknown Client)",
651 me.name, lsender, me.name);
652 }
653
654 /*
655 *
656 * parc number of arguments ('sender' counted as one!)
657 * parv[0] pointer to 'sender' (may point to empty string) (not used)
658 * parv[1]..parv[parc-1]
659 * pointers to additional parameters, this is a NULL
660 * terminated list (parv[parc] == NULL).
661 *
662 * *WARNING*
663 * Numerics are mostly error reports. If there is something
664 * wrong with the message, just *DROP* it! Don't even think of
665 * sending back a neat error message -- big danger of creating
666 * a ping pong error message...
667 *
668 * Rewritten by Nemesi, Jan 1999, to support numeric nicks in parv[1]
669 *
670 * Called when we get a numeric message from a remote _server_ and we are
671 * supposed to forward it somewhere. Note that we always ignore numerics sent
672 * to 'me' and simply drop the message if we can't handle with this properly:
673 * the savvy approach is NEVER generate an error in response to an... error :)
674 */
675 static void
676 handle_numeric(char numeric[], struct Client *client_p, struct Client *source_p,
677 int parc, char *parv[])
678 {
679 struct Client *target_p = NULL;
680 struct Channel *chptr = NULL;
681
682 /*
683 * Avoid trash, we need it to come from a server and have a target
684 */
685 if (parc < 2 || !IsServer(source_p))
686 return;
687
688 /*
689 * Who should receive this message ? Will we do something with it ?
690 * Note that we use findUser functions, so the target can't be neither
691 * a server, nor a channel (?) nor a list of targets (?) .. u2.10
692 * should never generate numeric replies to non-users anyway
693 * Ahem... it can be a channel actually, csc bots use it :\ --Nem
694 */
695 if (IsChanPrefix(*parv[1]))
696 chptr = hash_find_channel(parv[1]);
697 else
698 target_p = find_person(client_p, parv[1]);
699
700 if (((!target_p) || (target_p->from == client_p)) && !chptr)
701 return;
702
703 /*
704 * Remap low number numerics, not that I understand WHY.. --Nemesi
705 */
706 /*
707 * Numerics below 100 talk about the current 'connection', you're not
708 * connected to a remote server so it doesn't make sense to send them
709 * remotely - but the information they contain may be useful, so we
710 * remap them up. Weird, but true. -- Isomer
711 */
712 if (numeric[0] == '0')
713 numeric[0] = '1';
714
715 if (target_p)
716 {
717 /* Fake it for server hiding, if its our client */
718 if (ConfigServerHide.hide_servers && MyClient(target_p) &&
719 !HasUMode(target_p, UMODE_OPER))
720 sendto_one(target_p, ":%s %s %s %s", me.name, numeric, target_p->name, parv[2]);
721 else
722 sendto_one(target_p, ":%s %s %s %s", ID_or_name(source_p, target_p),
723 numeric, ID_or_name(target_p, target_p), parv[2]);
724 }
725 else
726 sendto_channel_local(ALL_MEMBERS, 0, chptr, ":%s %s %s %s",
727 source_p->name, numeric, chptr->chname, parv[2]);
728 }
729
730 /* m_not_oper()
731 * inputs -
732 * output -
733 * side effects - just returns a nastyogram to given user
734 */
735 int
736 m_not_oper(struct Client *client_p, struct Client *source_p,
737 int parc, char *parv[])
738 {
739 sendto_one(source_p, form_str(ERR_NOPRIVILEGES),
740 me.name, source_p->name);
741 return 0;
742 }
743
744 int
745 m_unregistered(struct Client *client_p, struct Client *source_p,
746 int parc, char *parv[])
747 {
748 sendto_one(source_p, form_str(ERR_NOTREGISTERED), me.name,
749 source_p->name[0] ? source_p->name : "*");
750 return 0;
751 }
752
753 int
754 m_registered(struct Client *client_p, struct Client *source_p,
755 int parc, char *parv[])
756 {
757 sendto_one(source_p, form_str(ERR_ALREADYREGISTRED),
758 me.name, source_p->name);
759 return 0;
760 }
761
762 int
763 m_ignore(struct Client *client_p, struct Client *source_p,
764 int parc, char *parv[])
765 {
766 return 0;
767 }

Properties

Name Value
svn:eol-style native
svn:keywords Id Revision