irc-write.c: Clean up code and add more documentation comments
[ngircd-alex.git] / src / ngircd / irc-write.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001-2013 Alexander Barton (alex@barton.de) and Contributors.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * Please read the file COPYING, README and AUTHORS for more information.
10  */
11
12 #include "portab.h"
13
14 /**
15  * @file
16  * Sending IRC commands over the network
17  */
18
19 #include "imp.h"
20 #include <assert.h>
21 #ifdef PROTOTYPES
22 #       include <stdarg.h>
23 #else
24 #       include <varargs.h>
25 #endif
26 #include <stdio.h>
27 #include <string.h>
28
29 #include "defines.h"
30 #include "conn-func.h"
31 #include "channel.h"
32
33 #include "exp.h"
34 #include "irc-write.h"
35
36 #define SEND_TO_USER 1
37 #define SEND_TO_SERVER 2
38
39 static const char *Get_Prefix PARAMS((CLIENT *Target, CLIENT *Client));
40 static void cb_writeStrServersPrefixFlag PARAMS((CLIENT *Client,
41                                          CLIENT *Prefix, void *Buffer));
42 static void Send_Marked_Connections PARAMS((CLIENT *Prefix, const char *Buffer));
43
44 /**
45  * Send a message to a client.
46  *
47  * @param Client The target client.
48  * @param Format Format string.
49  * @return CONNECTED or DISCONNECTED.
50  */
51 #ifdef PROTOTYPES
52 GLOBAL bool
53 IRC_WriteStrClient( CLIENT *Client, const char *Format, ... )
54 #else
55 GLOBAL bool
56 IRC_WriteStrClient( Client, Format, va_alist )
57 CLIENT *Client;
58 const char *Format;
59 va_dcl
60 #endif
61 {
62         char buffer[1000];
63         va_list ap;
64
65         assert(Client != NULL);
66         assert(Format != NULL);
67
68 #ifdef PROTOTYPES
69         va_start(ap, Format);
70 #else
71         va_start(ap);
72 #endif
73         vsnprintf(buffer, 1000, Format, ap);
74         va_end(ap);
75
76         return IRC_WriteStrClientPrefix(Client, Client_ThisServer(),
77                                         "%s", buffer);
78 }
79
80 /**
81  * Send a message to a client using a specific prefix.
82  *
83  * @param Client The target client.
84  * @param Prefix The prefix to use.
85  * @param Format Format string.
86  * @return CONNECTED or DISCONNECTED.
87  */
88 #ifdef PROTOTYPES
89 GLOBAL bool
90 IRC_WriteStrClientPrefix(CLIENT *Client, CLIENT *Prefix, const char *Format, ...)
91 #else
92 GLOBAL bool
93 IRC_WriteStrClientPrefix(Client, Prefix, Format, va_alist)
94 CLIENT *Client;
95 CLIENT *Prefix;
96 const char *Format;
97 va_dcl
98 #endif
99 {
100         /* send text to local and remote clients */
101
102         char buffer[1000];
103         va_list ap;
104
105         assert( Client != NULL );
106         assert( Format != NULL );
107         assert( Prefix != NULL );
108
109 #ifdef PROTOTYPES
110         va_start( ap, Format );
111 #else
112         va_start( ap );
113 #endif
114         vsnprintf( buffer, 1000, Format, ap );
115         va_end( ap );
116
117         return Conn_WriteStr(Client_Conn(Client_NextHop(Client)), ":%s %s",
118                         Get_Prefix(Client_NextHop(Client), Prefix), buffer);
119 }
120
121 /**
122  * Send a message to all client in a channel.
123  *
124  * The message is only sent once per remote server.
125  *
126  * @param Client The sending client, excluded while forwarding the message.
127  * @param Channel The target channel.
128  * @param Remote If not set, the message is sent to local clients only.
129  * @param Format Format string.
130  */
131 #ifdef PROTOTYPES
132 GLOBAL void
133 IRC_WriteStrChannel(CLIENT *Client, CHANNEL *Chan, bool Remote,
134                     const char *Format, ...)
135 #else
136 GLOBAL void
137 IRC_WriteStrChannel(Client, Chan, Remote, Format, va_alist)
138 CLIENT *Client;
139 CHANNEL *Chan;
140 bool Remote;
141 const char *Format;
142 va_dcl
143 #endif
144 {
145         char buffer[1000];
146         va_list ap;
147
148         assert( Client != NULL );
149         assert( Format != NULL );
150
151 #ifdef PROTOTYPES
152         va_start( ap, Format );
153 #else
154         va_start( ap );
155 #endif
156         vsnprintf( buffer, 1000, Format, ap );
157         va_end( ap );
158
159         IRC_WriteStrChannelPrefix(Client, Chan, Client_ThisServer(),
160                                   Remote, "%s", buffer);
161 }
162
163 /**
164  * Send a message to all client in a channel using a specific prefix.
165  *
166  * The message is only sent once per remote server.
167  *
168  * @param Client The sending client, excluded while forwarding the message.
169  * @param Channel The target channel.
170  * @param Prefix The prefix to use.
171  * @param Remote If not set, the message is sent to local clients only.
172  * @param Format Format string.
173  */
174 #ifdef PROTOTYPES
175 GLOBAL void
176 IRC_WriteStrChannelPrefix(CLIENT *Client, CHANNEL *Chan, CLIENT *Prefix,
177                           bool Remote, const char *Format, ...)
178 #else
179 GLOBAL void
180 IRC_WriteStrChannelPrefix(Client, Chan, Prefix, Remote, Format, va_alist)
181 CLIENT *Client;
182 CHANNEL *Chan;
183 CLIENT *Prefix;
184 bool Remote;
185 const char *Format;
186 va_dcl
187 #endif
188 {
189         char buffer[1000];
190         CL2CHAN *cl2chan;
191         CONN_ID conn;
192         CLIENT *c;
193         va_list ap;
194
195         assert( Client != NULL );
196         assert( Chan != NULL );
197         assert( Prefix != NULL );
198         assert( Format != NULL );
199
200 #ifdef PROTOTYPES
201         va_start( ap, Format );
202 #else
203         va_start( ap  );
204 #endif
205         vsnprintf( buffer, 1000, Format, ap );
206         va_end( ap );
207
208         Conn_ClearFlags( );
209
210         cl2chan = Channel_FirstMember( Chan );
211         while(cl2chan) {
212                 c = Channel_GetClient( cl2chan );
213                 if (!Remote) {
214                         if (Client_Conn(c) <= NONE)
215                                 c = NULL;
216                         else if(Client_Type(c) == CLIENT_SERVER)
217                                 c = NULL;
218                 }
219                 if(c)
220                         c = Client_NextHop(c);
221
222                 if(c && c != Client) {
223                         /* Ok, another Client */
224                         conn = Client_Conn(c);
225                         if (Client_Type(c) == CLIENT_SERVER)
226                                 Conn_SetFlag(conn, SEND_TO_SERVER);
227                         else
228                                 Conn_SetFlag(conn, SEND_TO_USER);
229                 }
230                 cl2chan = Channel_NextMember(Chan, cl2chan);
231         }
232         Send_Marked_Connections(Prefix, buffer);
233 }
234
235 /**
236  * Send a message to all the servers in the network.
237  *
238  * @param Client The sending client, excluded while forwarding the message.
239  * @param Format Format string.
240  */
241 #ifdef PROTOTYPES
242 GLOBAL void
243 IRC_WriteStrServers(CLIENT *ExceptOf, const char *Format, ...)
244 #else
245 GLOBAL void
246 IRC_WriteStrServers(ExceptOf, Format, va_alist)
247 CLIENT *ExceptOf;
248 const char *Format;
249 va_dcl
250 #endif
251 {
252         char buffer[1000];
253         va_list ap;
254
255         assert( Format != NULL );
256
257 #ifdef PROTOTYPES
258         va_start( ap, Format );
259 #else
260         va_start( ap );
261 #endif
262         vsnprintf( buffer, 1000, Format, ap );
263         va_end( ap );
264
265         IRC_WriteStrServersPrefix(ExceptOf, Client_ThisServer(), "%s", buffer);
266 }
267
268 /**
269  * Send a message to all the servers in the network using a specific prefix.
270  *
271  * @param Client The sending client, excluded while forwarding the message.
272  * @param Prefix The prefix to use.
273  * @param Format Format string.
274  */
275 #ifdef PROTOTYPES
276 GLOBAL void
277 IRC_WriteStrServersPrefix(CLIENT *ExceptOf, CLIENT *Prefix,
278                           const char *Format, ...)
279 #else
280 GLOBAL void
281 IRC_WriteStrServersPrefix(ExceptOf, Prefix, Format, va_alist)
282 CLIENT *ExceptOf;
283 CLIENT *Prefix;
284 const char *Format;
285 va_dcl
286 #endif
287 {
288         char buffer[1000];
289         va_list ap;
290
291         assert( Format != NULL );
292         assert( Prefix != NULL );
293
294 #ifdef PROTOTYPES
295         va_start( ap, Format );
296 #else
297         va_start( ap );
298 #endif
299         vsnprintf( buffer, 1000, Format, ap );
300         va_end( ap );
301
302         IRC_WriteStrServersPrefixFlag( ExceptOf, Prefix, '\0', "%s", buffer );
303 }
304
305 /**
306  * Send a message to all the servers in the network using a specific prefix
307  * and matching a "client flag".
308  *
309  * @param Client The sending client, excluded while forwarding the message.
310  * @param Prefix The prefix to use.
311  * @param Flag Client flag that must be set on the target.
312  * @param Format Format string.
313  */
314 #ifdef PROTOTYPES
315 GLOBAL void
316 IRC_WriteStrServersPrefixFlag(CLIENT *ExceptOf, CLIENT *Prefix, char Flag,
317                               const char *Format, ...)
318 #else
319 GLOBAL void
320 IRC_WriteStrServersPrefixFlag(ExceptOf, Prefix, Flag, Format, va_alist)
321 CLIENT *ExceptOf;
322 CLIENT *Prefix;
323 char Flag;
324 const char *Format;
325 va_dcl
326 #endif
327 {
328         char buffer[1000];
329         va_list ap;
330
331         assert( Format != NULL );
332         assert( Prefix != NULL );
333
334 #ifdef PROTOTYPES
335         va_start( ap, Format );
336 #else
337         va_start( ap );
338 #endif
339         vsnprintf( buffer, 1000, Format, ap );
340         va_end( ap );
341
342         IRC_WriteStrServersPrefixFlag_CB(ExceptOf, Prefix, Flag,
343                                          cb_writeStrServersPrefixFlag, buffer);
344 }
345
346 /**
347  * Send a message to all the servers in the network using a specific prefix
348  * and matching a "client flag" using a callback function.
349  *
350  * @param Client The sending client, excluded while forwarding the message.
351  * @param Prefix The prefix to use.
352  * @param Flag Client flag that must be set on the target.
353  * @param callback Callback function.
354  * @param Format Format string.
355  */
356 GLOBAL void
357 IRC_WriteStrServersPrefixFlag_CB(CLIENT *ExceptOf, CLIENT *Prefix, char Flag,
358                 void (*callback)(CLIENT *, CLIENT *, void *), void *cb_data)
359 {
360         CLIENT *c;
361
362         c = Client_First();
363         while(c) {
364                 if (Client_Type(c) == CLIENT_SERVER && Client_Conn(c) > NONE &&
365                     c != Client_ThisServer() && c != ExceptOf) {
366                         /* Found a target server, do the flags match? */
367                         if (Flag == '\0' || Client_HasFlag(c, Flag))
368                                 callback(c, Prefix, cb_data);
369                 }
370                 c = Client_Next(c);
371         }
372 }
373
374 /**
375  * Send a message to all "related" clients.
376  *
377  * Related clients are the one that share one ore more channels with the client
378  * sending this message.
379  *
380  * The message is only sent once per remote server.
381  *
382  * @param Client The sending client, excluded while forwarding the message.
383  * @param Prefix The prefix to use.
384  * @param Remote If not set, the message is sent to local clients only.
385  * @param Format Format string.
386  */
387 #ifdef PROTOTYPES
388 GLOBAL void
389 IRC_WriteStrRelatedPrefix(CLIENT *Client, CLIENT *Prefix, bool Remote,
390                           const char *Format, ...)
391 #else
392 GLOBAL void
393 IRC_WriteStrRelatedPrefix(Client, Prefix, Remote, Format, va_alist)
394 CLIENT *Client;
395 CLIENT *Prefix;
396 bool Remote;
397 const char *Format;
398 va_dcl
399 #endif
400 {
401         CL2CHAN *chan_cl2chan, *cl2chan;
402         char buffer[1000];
403         CHANNEL *chan;
404         CONN_ID conn;
405         va_list ap;
406         CLIENT *c;
407
408         assert( Client != NULL );
409         assert( Prefix != NULL );
410         assert( Format != NULL );
411
412 #ifdef PROTOTYPES
413         va_start( ap, Format );
414 #else
415         va_start( ap );
416 #endif
417         vsnprintf( buffer, 1000, Format, ap );
418         va_end( ap );
419
420         Conn_ClearFlags( );
421
422         chan_cl2chan = Channel_FirstChannelOf( Client );
423         while( chan_cl2chan )
424         {
425                 chan = Channel_GetChannel( chan_cl2chan );
426                 cl2chan = Channel_FirstMember( chan );
427                 while( cl2chan )
428                 {
429                         c = Channel_GetClient( cl2chan );
430                         if( ! Remote )
431                         {
432                                 if( Client_Conn( c ) <= NONE ) c = NULL;
433                                 else if( Client_Type( c ) == CLIENT_SERVER ) c = NULL;
434                         }
435                         if( c ) c = Client_NextHop( c );
436
437                         if( c && ( c != Client ))
438                         {
439                                 conn = Client_Conn( c );
440                                 if( Client_Type( c ) == CLIENT_SERVER ) Conn_SetFlag( conn, SEND_TO_SERVER );
441                                 else Conn_SetFlag( conn, SEND_TO_USER );
442                         }
443                         cl2chan = Channel_NextMember( chan, cl2chan );
444                 }
445
446                 chan_cl2chan = Channel_NextChannelOf( Client, chan_cl2chan );
447         }
448         Send_Marked_Connections(Prefix, buffer);
449 } /* IRC_WriteStrRelatedPrefix */
450
451 /**
452  * Send WALLOPS message.
453  *
454  * @param Client The sending client, excluded while forwarding the message.
455  * @param From The (remote) sender of the message.
456  * @param Format Format string.
457 */
458 #ifdef PROTOTYPES
459 GLOBAL void
460 IRC_SendWallops(CLIENT *Client, CLIENT *From, const char *Format, ...)
461 #else
462 GLOBAL void
463 IRC_SendWallops(Client, From, Format, va_alist )
464 CLIENT *Client;
465 CLIENT *From;
466 const char *Format;
467 va_dcl
468 #endif
469 {
470         va_list ap;
471         char msg[1000];
472         CLIENT *to;
473
474 #ifdef PROTOTYPES
475         va_start(ap, Format);
476 #else
477         va_start(ap);
478 #endif
479         vsnprintf(msg, 1000, Format, ap);
480         va_end(ap);
481
482         for (to=Client_First(); to != NULL; to=Client_Next(to)) {
483                 if (Client_Conn(to) == NONE) /* no local connection */
484                         continue;
485
486                 switch (Client_Type(to)) {
487                 case CLIENT_USER:
488                         if (Client_HasMode(to, 'w'))
489                                 IRC_WriteStrClientPrefix(to, From,
490                                                          "WALLOPS :%s", msg);
491                                 break;
492                 case CLIENT_SERVER:
493                         if (to != Client)
494                                 IRC_WriteStrClientPrefix(to, From,
495                                                          "WALLOPS :%s", msg);
496                                 break;
497                 }
498         }
499 } /* IRC_SendWallops */
500
501 /**
502  * Set a "penalty time" for an IRC client.
503  *
504  * Note: penalty times are never set for server links!
505  *
506  * @param Client The client.
507  * @param Seconds The additional "penalty time" to enforce.
508  */
509 GLOBAL void
510 IRC_SetPenalty( CLIENT *Client, time_t Seconds )
511 {
512         CONN_ID c;
513
514         assert( Client != NULL );
515         assert( Seconds > 0 );
516
517         if( Client_Type( Client ) == CLIENT_SERVER ) return;
518
519         c = Client_Conn( Client );
520         if (c > NONE)
521                 Conn_SetPenalty(c, Seconds);
522 } /* IRC_SetPenalty */
523
524 static const char *
525 Get_Prefix(CLIENT *Target, CLIENT *Client)
526 {
527         assert (Target != NULL);
528         assert (Client != NULL);
529
530         if (Client_Type(Target) == CLIENT_SERVER)
531                 return Client_ID(Client);
532         else
533                 return Client_MaskCloaked(Client);
534 } /* Get_Prefix */
535
536 static void
537 cb_writeStrServersPrefixFlag(CLIENT *Client, CLIENT *Prefix, void *Buffer)
538 {
539         IRC_WriteStrClientPrefix(Client, Prefix, "%s", Buffer);
540 } /* cb_writeStrServersPrefixFlag */
541
542 /**
543  * Send a message to all marked connections using a specific prefix.
544  *
545  * @param Prefix The prefix to use.
546  * @param Buffer The message to send.
547  */
548 static void
549 Send_Marked_Connections(CLIENT *Prefix, const char *Buffer)
550 {
551         CONN_ID conn;
552
553         assert(Prefix != NULL);
554         assert(Buffer != NULL);
555
556         conn = Conn_First();
557         while (conn != NONE) {
558                 if (Conn_Flag(conn) == SEND_TO_SERVER)
559                         Conn_WriteStr(conn, ":%s %s",
560                                       Client_ID(Prefix), Buffer);
561                 else if (Conn_Flag(conn) == SEND_TO_USER)
562                         Conn_WriteStr(conn, ":%s %s",
563                                       Client_MaskCloaked(Prefix), Buffer);
564                 conn = Conn_Next(conn);
565         }
566 }
567
568 /* -eof- */