]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/irc-mode.c
- Parser auf Befehlstabelle umgestellt.
[ngircd-alex.git] / src / ngircd / irc-mode.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de)
4  *
5  * Dieses Programm ist freie Software. Sie koennen es unter den Bedingungen
6  * der GNU General Public License (GPL), wie von der Free Software Foundation
7  * herausgegeben, weitergeben und/oder modifizieren, entweder unter Version 2
8  * der Lizenz oder (wenn Sie es wuenschen) jeder spaeteren Version.
9  * Naehere Informationen entnehmen Sie bitter der Datei COPYING. Eine Liste
10  * der an ngIRCd beteiligten Autoren finden Sie in der Datei AUTHORS.
11  *
12  * $Id: irc-mode.c,v 1.16 2002/11/30 15:04:57 alex Exp $
13  *
14  * irc-mode.c: IRC-Befehle zur Mode-Aenderung (MODE, AWAY, ...)
15  */
16
17
18 #include "portab.h"
19
20 #include "imp.h"
21 #include <assert.h>
22 #include <string.h>
23
24 #include "conn.h"
25 #include "client.h"
26 #include "channel.h"
27 #include "defines.h"
28 #include "irc-write.h"
29 #include "lists.h"
30 #include "log.h"
31 #include "parse.h"
32 #include "messages.h"
33 #include "resolve.h"
34 #include "conf.h"
35
36 #include "exp.h"
37 #include "irc-mode.h"
38
39
40 LOCAL BOOLEAN Add_Invite PARAMS(( CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, CHAR *Pattern ));
41 LOCAL BOOLEAN Add_Ban PARAMS(( CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, CHAR *Pattern ));
42
43 LOCAL BOOLEAN Del_Invite PARAMS(( CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, CHAR *Pattern ));
44 LOCAL BOOLEAN Del_Ban PARAMS(( CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, CHAR *Pattern ));
45
46 LOCAL BOOLEAN Send_ListChange PARAMS(( CHAR *Mode, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, CHAR *Mask ));
47
48
49 GLOBAL BOOLEAN
50 IRC_MODE( CLIENT *Client, REQUEST *Req )
51 {
52         CHAR *mode_ptr, the_modes[CLIENT_MODE_LEN], x[2];
53         CLIENT *cl, *chan_cl, *prefix;
54         BOOLEAN set, ok, modeok;
55         CHANNEL *chan;
56         
57         assert( Client != NULL );
58         assert( Req != NULL );
59
60         cl = chan_cl = prefix = NULL;
61         chan = NULL;
62
63         /* Keine Parameter? */
64         if( Req->argc < 1 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
65
66         /* Ziel suchen: Client bzw. Channel */
67         if( Client_IsValidNick( Req->argv[0] )) cl = Client_Search( Req->argv[0] );
68         if( Channel_IsValidName( Req->argv[0] )) chan = Channel_Search( Req->argv[0] );
69
70         /* Kein Ziel gefunden? */
71         if(( ! cl ) && ( ! chan )) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->argv[0] );
72
73         assert(( cl && chan ) != TRUE );
74
75         /* Falsche Anzahl Parameter? */
76         if(( cl ) && ( Req->argc > 2 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
77         if(( chan ) && ( Req->argc > 3 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
78
79         /* Prefix fuer Antworten etc. ermitteln */
80         if( Client_Type( Client ) == CLIENT_SERVER )
81         {
82                 prefix = Client_Search( Req->prefix );
83                 if( ! prefix ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix );
84         }
85         else prefix = Client;
86         
87         if(( chan ) && (( Req->argc == 2 ) || ( Req->argc == 3 )))
88         {
89                 /* pruefen, ob "Listen-Operation": Invite, Ban */
90                 if(( Req->argv[1][0] == '-'  ) || ( Req->argv[1][0] == '+' )) mode_ptr = &Req->argv[1][1];
91                 else mode_ptr = &Req->argv[1][0];
92
93                 if( Req->argc == 2 )
94                 {
95                         /* Liste anzeigen */
96                         if( *mode_ptr == 'I' ) return Lists_ShowInvites( prefix, chan );
97                         if( *mode_ptr == 'b' ) return Lists_ShowBans( prefix, chan );
98                 }
99                 else
100                 {
101                         /* Listen veraendern */
102
103                         if( Client_Type( Client ) == CLIENT_USER )
104                         {
105                                 /* Ist der User Channel-Operator? */
106                                 modeok = FALSE;
107                                 if( strchr( Channel_UserModes( chan, Client ), 'o' )) modeok = TRUE;
108                                 if( Conf_OperCanMode )
109                                 {
110                                         /* auch IRC-Operatoren duerfen MODE verwenden */
111                                         if( Client_OperByMe( Client )) modeok = TRUE;
112                                 }
113
114                                 if( ! modeok )
115                                 {
116                                         Log( LOG_DEBUG, "Can't change modes: \"%s\" is not operator on %s!", Client_ID( Client ), Channel_Name( chan ));
117                                         return IRC_WriteStrClient( Client, ERR_CHANOPRIVSNEEDED_MSG, Client_ID( Client ), Channel_Name( chan ));
118                                 }
119                         }
120                         
121                         if( Req->argv[1][0] == '+' )
122                         {
123                                 /* Listen-Eintrag hinzufuegen */
124                                 if( *mode_ptr == 'I' ) return Add_Invite( prefix, Client, chan, Req->argv[2] );
125                                 if( *mode_ptr == 'b' ) return Add_Ban( prefix, Client, chan, Req->argv[2] );
126                         }
127                         else if( Req->argv[1][0] == '-' )
128                         {
129                                 /* Listen-Eintrag loeschen */
130                                 if( *mode_ptr == 'I' ) return Del_Invite( prefix, Client, chan, Req->argv[2] );
131                                 if( *mode_ptr == 'b' ) return Del_Ban( prefix, Client, chan, Req->argv[2] );
132                         }
133                 }
134         }
135
136         /* Client ermitteln, wenn bei Channel-Modes mit 3 Parametern */
137         if(( chan ) && (Req->argc == 3 ))
138         {
139                 chan_cl = Client_Search( Req->argv[2] );
140                 if( ! chan_cl ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->argv[0] );
141         }
142         
143         /* Wenn Anfragender ein User ist: Zugriff erlaubt? */
144         if( Client_Type( Client ) == CLIENT_USER )
145         {
146                 if( cl )
147                 {
148                         /* MODE ist nur fuer sich selber zulaessig! */
149                         if( cl != Client ) return IRC_WriteStrClient( Client, ERR_USERSDONTMATCH_MSG, Client_ID( Client ));
150                 }
151                 if( chan )
152                 {
153                         /* Darf der User die Channel-Modes ermitteln? */
154                 }
155         }
156
157         /* Werden die Modes "nur" erfragt? */
158         if(( cl ) && ( Req->argc == 1 )) return IRC_WriteStrClient( Client, RPL_UMODEIS_MSG, Client_ID( Client ), Client_Modes( cl ));
159         if(( chan ) && ( Req->argc == 1 )) return IRC_WriteStrClient( Client, RPL_CHANNELMODEIS_MSG, Client_ID( Client ), Channel_Name( chan ), Channel_Modes( chan ));
160
161         mode_ptr = Req->argv[1];
162
163         /* Sollen Modes gesetzt oder geloescht werden? */
164         if( cl )
165         {
166                 if( *mode_ptr == '+' ) set = TRUE;
167                 else if( *mode_ptr == '-' ) set = FALSE;
168                 else return IRC_WriteStrClient( Client, ERR_UMODEUNKNOWNFLAG_MSG, Client_ID( Client ));
169                 mode_ptr++;
170         }
171         else
172         {
173                 if( *mode_ptr == '-' ) set = FALSE;
174                 else set = TRUE;
175                 if(( *mode_ptr == '-' ) || ( *mode_ptr == '+' )) mode_ptr++;
176         }
177
178         /* Reply-String mit Aenderungen vorbereiten */
179         if( set ) strcpy( the_modes, "+" );
180         else strcpy( the_modes, "-" );
181
182         ok = TRUE;
183         x[1] = '\0';
184         while( *mode_ptr )
185         {
186                 x[0] = '\0';
187                 if( Client_Type( Client ) == CLIENT_SERVER )
188                 {
189                         /* Befehl kommt von einem Server, daher
190                          * trauen wir ihm "unbesehen" ... */
191                         x[0] = *mode_ptr;
192                 }
193                 else
194                 {
195                         /* Modes validieren */
196                         if( cl )
197                         {
198                                 /* User-Modes */
199                                 switch( *mode_ptr )
200                                 {
201                                         case 'i':
202                                                 /* invisible */
203                                                 x[0] = 'i';
204                                                 break;
205                                         case 'o':
206                                                 /* operator (kann nur geloescht werden) */
207                                                 if( ! set )
208                                                 {
209                                                         Client_SetOperByMe( Client, FALSE );
210                                                         x[0] = 'o';
211                                                 }
212                                                 else ok = IRC_WriteStrClient( Client, ERR_NOPRIVILEGES_MSG, Client_ID( Client ));
213                                                 break;
214                                         case 'r':
215                                                 /* restricted (kann nur gesetzt werden) */
216                                                 if( set ) x[0] = 'r';
217                                                 else ok = IRC_WriteStrClient( Client, ERR_RESTRICTED_MSG, Client_ID( Client ));
218                                                 break;
219                                         case 's':
220                                                 /* server messages */
221                                                 x[0] = 's';
222                                                 break;
223                                         default:
224                                                 Log( LOG_DEBUG, "Unknown mode \"%c%c\" from \"%s\"!?", set ? '+' : '-', *mode_ptr, Client_ID( Client ));
225                                                 ok = IRC_WriteStrClient( Client, ERR_UMODEUNKNOWNFLAG2_MSG, Client_ID( Client ), set ? '+' : '-', *mode_ptr );
226                                                 x[0] = '\0';
227                                 }
228                         }
229                         if( chan )
230                         {
231                                 /* Ist der User ein Channel Operator? */
232                                 modeok = FALSE;
233                                 if( strchr( Channel_UserModes( chan, Client ), 'o' )) modeok = TRUE;
234                                 if( Conf_OperCanMode )
235                                 {
236                                         /* auch IRC-Operatoren duerfen MODE verwenden */
237                                         if( Client_OperByMe( Client )) modeok = TRUE;
238                                 }
239
240                                 if( ! modeok )
241                                 {
242                                         Log( LOG_DEBUG, "Can't change modes: \"%s\" is not operator on %s!", Client_ID( Client ), Channel_Name( chan ));
243                                         ok = IRC_WriteStrClient( Client, ERR_CHANOPRIVSNEEDED_MSG, Client_ID( Client ), Channel_Name( chan ));
244                                         break;
245                                 }
246                                 
247                                 /* Channel-Modes oder Channel-User-Modes */
248                                 if( chan_cl )
249                                 {
250                                         /* Channel-User-Modes */
251                                         switch( *mode_ptr )
252                                         {
253                                                 case 'o':
254                                                         /* Channel Operator */
255                                                         x[0] = 'o';
256                                                         break;
257                                                 case 'v':
258                                                         /* Voice */
259                                                         x[0] = 'v';
260                                                         break;
261                                                 default:
262                                                         Log( LOG_DEBUG, "Unknown channel-user-mode \"%c%c\" from \"%s\" on \"%s\" at %s!?", set ? '+' : '-', *mode_ptr, Client_ID( Client ), Client_ID( chan_cl ), Channel_Name( chan ));
263                                                         ok = IRC_WriteStrClient( Client, ERR_UMODEUNKNOWNFLAG2_MSG, Client_ID( Client ), set ? '+' : '-', *mode_ptr );
264                                                         x[0] = '\0';
265                                         }
266                                 }
267                                 else
268                                 {
269                                         /* Channel-Modes */
270                                         switch( *mode_ptr )
271                                         {
272                                                 case 'i':
273                                                         /* Invite-Only */
274                                                         x[0] = 'i';
275                                                         break;
276                                                 case 'm':
277                                                         /* Moderated */
278                                                         x[0] = 'm';
279                                                         break;
280                                                 case 'n':
281                                                         /* kein Schreiben in den Channel von aussen */
282                                                         x[0] = 'n';
283                                                         break;
284                                                 case 't':
285                                                         /* Topic Lock */
286                                                         x[0] = 't';
287                                                         break;
288                                                 case 'P':
289                                                         /* Persistent. Dieses Flag darf nur von IRC-Operatoren gesetzt werden! */
290                                                         if( set && ( ! Client_OperByMe( Client )))
291                                                         {
292                                                                 ok = IRC_WriteStrClient( Client, ERR_NOPRIVILEGES_MSG, Client_ID( Client ));
293                                                         }
294                                                         else x[0] = 'P';
295                                                         break;
296                                                 default:
297                                                         Log( LOG_DEBUG, "Unknown channel-mode \"%c%c\" from \"%s\" at %s!?", set ? '+' : '-', *mode_ptr, Client_ID( Client ), Channel_Name( chan ));
298                                                         ok = IRC_WriteStrClient( Client, ERR_UMODEUNKNOWNFLAG2_MSG, Client_ID( Client ), set ? '+' : '-', *mode_ptr );
299                                                         x[0] = '\0';
300                                         }
301                                 }
302                         }
303                 }
304                 if( ! ok ) break;
305                 
306                 mode_ptr++;
307                 if( ! x[0] ) continue;
308
309                 /* Okay, gueltigen Mode gefunden */
310                 if( cl )
311                 {
312                         /* Es geht um User-Modes */
313                         if( set )
314                         {
315                                 /* Mode setzen. Wenn der Client ihn noch nicht hatte: merken */
316                                 if( Client_ModeAdd( cl, x[0] )) strcat( the_modes, x );
317                                 
318                         }
319                         else
320                         {
321                                 /* Modes geloescht. Wenn der Client ihn hatte: merken */
322                                 if( Client_ModeDel( cl, x[0] )) strcat( the_modes, x );
323                         }
324
325                         /* "nachbearbeiten" */
326                         if( x[0] == 'a' )
327                         {
328                                 /* away */
329                                 if( set ) Client_SetAway( cl, DEFAULT_AWAY_MSG );
330                                 else Client_SetAway( cl, NULL );
331                         }
332                 }
333                 if( chan )
334                 {
335                         /* Es geht um Channel-Modes oder Channel-User-Modes */
336                         if( chan_cl )
337                         {
338                                 /* Channel-User-Modes */
339                                 if( set )
340                                 {
341                                         /* Mode setzen. Wenn der Channel ihn noch nicht hatte: merken */
342                                         if( Channel_UserModeAdd( chan, chan_cl, x[0] )) strcat( the_modes, x );
343                                 }
344                                 else
345                                 {
346                                         /* Mode setzen. Wenn der Channel ihn noch nicht hatte: merken */
347                                         if( Channel_UserModeDel( chan, chan_cl, x[0] )) strcat( the_modes, x );
348                                 }
349                         }
350                         else
351                         {
352                                 /* Channel-Mode */
353                                 if( set )
354                                 {
355                                         /* Mode setzen. Wenn der Channel ihn noch nicht hatte: merken */
356                                         if( Channel_ModeAdd( chan, x[0] )) strcat( the_modes, x );
357                                 }
358                                 else
359                                 {
360                                         /* Mode setzen. Wenn der Channel ihn noch nicht hatte: merken */
361                                         if( Channel_ModeDel( chan, x[0] )) strcat( the_modes, x );
362                                 }
363                         }
364                 }
365         }
366
367         /* Wurden Modes geaendert? */
368         if( the_modes[1] )
369         {
370                 if( cl )
371                 {
372                         /* Client-Mode */
373                         if( Client_Type( Client ) == CLIENT_SERVER )
374                         {
375                                 /* Modes an andere Server forwarden */
376                                 IRC_WriteStrServersPrefix( Client, prefix, "MODE %s :%s", Client_ID( cl ), the_modes );
377                         }
378                         else
379                         {
380                                 /* Bestaetigung an Client schicken & andere Server informieren */
381                                 ok = IRC_WriteStrClientPrefix( Client, prefix, "MODE %s %s", Client_ID( cl ), the_modes );
382                                 IRC_WriteStrServersPrefix( Client, prefix, "MODE %s :%s", Client_ID( cl ), the_modes );
383                         }
384                         Log( LOG_DEBUG, "User \"%s\": Mode change, now \"%s\".", Client_Mask( cl ), Client_Modes( cl ));
385                 }
386                 if( chan )
387                 {
388                         /* Channel-Modes oder Channel-User-Mode */
389                         if( chan_cl )
390                         {
391                                 /* Channel-User-Mode */
392                                 if( Client_Type( Client ) == CLIENT_SERVER )
393                                 {
394                                         /* Modes an andere Server und Channel-User forwarden */
395                                         IRC_WriteStrServersPrefix( Client, prefix, "MODE %s %s :%s", Channel_Name( chan ), the_modes, Client_ID( chan_cl));
396                                         IRC_WriteStrChannelPrefix( Client, chan, prefix, FALSE, "MODE %s %s %s", Channel_Name( chan ), the_modes, Client_ID( chan_cl));
397                                 }
398                                 else
399                                 {
400                                         /* Bestaetigung an Client schicken & andere Server sowie Channel-User informieren */
401                                         ok = IRC_WriteStrClientPrefix( Client, prefix, "MODE %s %s %s", Channel_Name( chan ), the_modes, Client_ID( chan_cl));
402                                         IRC_WriteStrServersPrefix( Client, prefix, "MODE %s %s :%s", Channel_Name( chan ), the_modes, Client_ID( chan_cl));
403                                         IRC_WriteStrChannelPrefix( Client, chan, prefix, FALSE, "MODE %s %s %s", Channel_Name( chan ), the_modes, Client_ID( chan_cl));
404                                 }
405                                 Log( LOG_DEBUG, "User \"%s\" on %s: Mode change, now \"%s\".", Client_Mask( chan_cl), Channel_Name( chan ), Channel_UserModes( chan, chan_cl ));
406                         }
407                         else
408                         {
409                                 /* Channel-Mode */
410                                 if( Client_Type( Client ) == CLIENT_SERVER )
411                                 {
412                                         /* Modes an andere Server und Channel-User forwarden */
413                                         IRC_WriteStrServersPrefix( Client, prefix, "MODE %s :%s", Channel_Name( chan ), the_modes );
414                                         IRC_WriteStrChannelPrefix( Client, chan, prefix, FALSE, "MODE %s %s", Channel_Name( chan ), the_modes );
415                                 }
416                                 else
417                                 {
418                                         /* Bestaetigung an Client schicken & andere Server sowie Channel-User informieren */
419                                         ok = IRC_WriteStrClientPrefix( Client, prefix, "MODE %s %s", Channel_Name( chan ), the_modes );
420                                         IRC_WriteStrServersPrefix( Client, prefix, "MODE %s :%s", Channel_Name( chan ), the_modes );
421                                         IRC_WriteStrChannelPrefix( Client, chan, prefix, FALSE, "MODE %s %s", Channel_Name( chan ), the_modes );
422                                 }
423                                 Log( LOG_DEBUG, "Channel \"%s\": Mode change, now \"%s\".", Channel_Name( chan ), Channel_Modes( chan ));
424                         }
425                 }
426         }
427
428         return ok;
429 } /* IRC_MODE */
430
431
432 GLOBAL BOOLEAN
433 IRC_AWAY( CLIENT *Client, REQUEST *Req )
434 {
435         assert( Client != NULL );
436         assert( Req != NULL );
437
438         /* Falsche Anzahl Parameter? */
439         if( Req->argc > 1 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
440
441         if(( Req->argc == 1 ) && (Req->argv[0][0] ))
442         {
443                 /* AWAY setzen */
444                 Client_SetAway( Client, Req->argv[0] );
445                 IRC_WriteStrServersPrefix( Client, Client, "MODE %s :+a", Client_ID( Client ));
446                 return IRC_WriteStrClient( Client, RPL_NOWAWAY_MSG, Client_ID( Client ));
447         }
448         else
449         {
450                 /* AWAY loeschen */
451                 Client_SetAway( Client, NULL );
452                 IRC_WriteStrServersPrefix( Client, Client, "MODE %s :-a", Client_ID( Client ));
453                 return IRC_WriteStrClient( Client, RPL_UNAWAY_MSG, Client_ID( Client ));
454         }
455 } /* IRC_AWAY */
456
457
458 LOCAL BOOLEAN
459 Add_Invite( CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, CHAR *Pattern )
460 {
461         CHAR *mask;
462
463         assert( Client != NULL );
464         assert( Channel != NULL );
465         assert( Pattern != NULL );
466
467         mask = Lists_MakeMask( Pattern );
468
469         if( ! Lists_AddInvited( Prefix, mask, Channel, FALSE )) return CONNECTED;
470         return Send_ListChange( "+I", Prefix, Client, Channel, mask );
471 } /* Add_Invite */
472
473
474 LOCAL BOOLEAN
475 Add_Ban( CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, CHAR *Pattern )
476 {
477         CHAR *mask;
478
479         assert( Client != NULL );
480         assert( Channel != NULL );
481         assert( Pattern != NULL );
482
483         mask = Lists_MakeMask( Pattern );
484
485         if( ! Lists_AddBanned( Prefix, mask, Channel )) return CONNECTED;
486         return Send_ListChange( "+b", Prefix, Client, Channel, mask );
487 } /* Add_Ban */
488
489
490 LOCAL BOOLEAN
491 Del_Invite( CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, CHAR *Pattern )
492 {
493         CHAR *mask;
494
495         assert( Client != NULL );
496         assert( Channel != NULL );
497         assert( Pattern != NULL );
498
499         mask = Lists_MakeMask( Pattern );
500         Lists_DelInvited( mask, Channel );
501         return Send_ListChange( "-I", Prefix, Client, Channel, mask );
502 } /* Del_Invite */
503
504
505 LOCAL BOOLEAN
506 Del_Ban( CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, CHAR *Pattern )
507 {
508         CHAR *mask;
509
510         assert( Client != NULL );
511         assert( Channel != NULL );
512         assert( Pattern != NULL );
513
514         mask = Lists_MakeMask( Pattern );
515         Lists_DelBanned( mask, Channel );
516         return Send_ListChange( "-b", Prefix, Client, Channel, mask );
517 } /* Del_Ban */
518
519
520 LOCAL BOOLEAN
521 Send_ListChange( CHAR *Mode, CLIENT *Prefix, CLIENT *Client, CHANNEL *Channel, CHAR *Mask )
522 {
523         /* Bestaetigung an Client schicken & andere Server sowie Channel-User informieren */
524
525         BOOLEAN ok;
526
527         if( Client_Type( Client ) == CLIENT_USER )
528         {
529                 /* Bestaetigung an Client */
530                 ok = IRC_WriteStrClientPrefix( Client, Prefix, "MODE %s %s %s", Channel_Name( Channel ), Mode, Mask );
531         }
532         else ok = TRUE;
533
534         /* an andere Server */
535         IRC_WriteStrServersPrefix( Client, Prefix, "MODE %s %s %s", Channel_Name( Channel ), Mode, Mask );
536
537         /* und lokale User im Channel */
538         IRC_WriteStrChannelPrefix( Client, Channel, Prefix, FALSE, "MODE %s %s %s", Channel_Name( Channel ), Mode, Mask );
539         
540         return ok;
541 } /* Send_ListChange */
542
543
544 /* -eof- */