1NAME
2
3 POE::Component::Server::NNTP - A POE component that provides NNTP
4 server functionality.
5
6VERSION
7
8 version 1.06
9
10SYNOPSIS
11
12 use strict;
13 use POE qw(Component::Server::NNTP);
14
15 my %groups;
16
17 while(<DATA>) {
18 chomp;
19 push @{ $groups{'perl.cpan.testers'}->{'<perl.cpan.testers-381062@nntp.perl.org>'} }, $_;
20 }
21
22 my $nntpd = POE::Component::Server::NNTP->spawn(
23 alias => 'nntpd',
24 posting => 0,
25 port => 10119,
26 );
27
28 POE::Session->create(
29 package_states => [
30 'main' => [ qw(
31 _start
32 nntpd_connection
33 nntpd_disconnected
34 nntpd_cmd_post
35 nntpd_cmd_ihave
36 nntpd_cmd_slave
37 nntpd_cmd_newnews
38 nntpd_cmd_newgroups
39 nntpd_cmd_list
40 nntpd_cmd_group
41 nntpd_cmd_article
42 ) ],
43 ],
44 options => { trace => 0 },
45 );
46
47 $poe_kernel->run();
48 exit 0;
49
50 sub _start {
51 my ($kernel,$heap) = @_[KERNEL,HEAP];
52 $heap->{clients} = { };
53 $kernel->post( 'nntpd', 'register', 'all' );
54 return;
55 }
56
57 sub nntpd_connection {
58 my ($kernel,$heap,$client_id) = @_[KERNEL,HEAP,ARG0];
59 $heap->{clients}->{ $client_id } = { };
60 return;
61 }
62
63 sub nntpd_disconnected {
64 my ($kernel,$heap,$client_id) = @_[KERNEL,HEAP,ARG0];
65 delete $heap->{clients}->{ $client_id };
66 return;
67 }
68
69 sub nntpd_cmd_slave {
70 my ($kernel,$sender,$client_id) = @_[KERNEL,SENDER,ARG0];
71 $kernel->post( $sender, 'send_to_client', $client_id, '202 slave status noted' );
72 return;
73 }
74
75 sub nntpd_cmd_post {
76 my ($kernel,$sender,$client_id) = @_[KERNEL,SENDER,ARG0];
77 $kernel->post( $sender, 'send_to_client', $client_id, '440 posting not allowed' );
78 return;
79 }
80
81 sub nntpd_cmd_ihave {
82 my ($kernel,$sender,$client_id) = @_[KERNEL,SENDER,ARG0];
83 $kernel->post( $sender, 'send_to_client', $client_id, '435 article not wanted' );
84 return;
85 }
86
87 sub nntpd_cmd_newnews {
88 my ($kernel,$sender,$client_id) = @_[KERNEL,SENDER,ARG0];
89 $kernel->post( $sender, 'send_to_client', $client_id, '230 list of new articles follows' );
90 $kernel->post( $sender, 'send_to_client', $client_id, '.' );
91 return;
92 }
93
94 sub nntpd_cmd_newgroups {
95 my ($kernel,$sender,$client_id) = @_[KERNEL,SENDER,ARG0];
96 $kernel->post( $sender, 'send_to_client', $client_id, '231 list of new newsgroups follows' );
97 $kernel->post( $sender, 'send_to_client', $client_id, '.' );
98 return;
99 }
100
101 sub nntpd_cmd_list {
102 my ($kernel,$sender,$client_id) = @_[KERNEL,SENDER,ARG0];
103 $kernel->post( $sender, 'send_to_client', $client_id, '215 list of newsgroups follows' );
104 foreach my $group ( keys %groups ) {
105 my $reply = join ' ', $group, scalar keys %{ $groups{$group} }, 1, 'n';
106 $kernel->post( $sender, 'send_to_client', $client_id, $reply );
107 }
108 $kernel->post( $sender, 'send_to_client', $client_id, '.' );
109 return;
110 }
111
112 sub nntpd_cmd_group {
113 my ($kernel,$sender,$client_id,$group) = @_[KERNEL,SENDER,ARG0,ARG1];
114 unless ( $group or exists $groups{lc $group} ) {
115 $kernel->post( $sender, 'send_to_client', $client_id, '411 no such news group' );
116 return;
117 }
118 $group = lc $group;
119 $kernel->post( $sender, 'send_to_client', $client_id, "211 1 1 1 $group selected" );
120 $_[HEAP]->{clients}->{ $client_id } = { group => $group };
121 return;
122 }
123
124 sub nntpd_cmd_article {
125 my ($kernel,$sender,$client_id,$article) = @_[KERNEL,SENDER,ARG0,ARG1];
126 my $group = 'perl.cpan.testers';
127 if ( !$article and !defined $_[HEAP]->{clients}->{ $client_id}->{group} ) {
128 $kernel->post( $sender, 'send_to_client', $client_id, '412 no newsgroup selected' );
129 return;
130 }
131 $article = 1 unless $article;
132 if ( $article !~ /^<.*>$/ and $article ne '1' ) {
133 $kernel->post( $sender, 'send_to_client', $client_id, '423 no such article number' );
134 return;
135 }
136 if ( $article =~ /^<.*>$/ and !defined $groups{$group}->{$article} ) {
137 $kernel->post( $sender, 'send_to_client', $client_id, '430 no such article found' );
138 return;
139 }
140 foreach my $msg_id ( keys %{ $groups{$group} } ) {
141 $kernel->post( $sender, 'send_to_client', $client_id, "220 1 $msg_id article retrieved - head and body follow" );
142 $kernel->post( $sender, 'send_to_client', $client_id, $_ ) for @{ $groups{$group}->{$msg_id } };
143 $kernel->post( $sender, 'send_to_client', $client_id, '.' );
144 }
145 return;
146 }
147
148 __END__
149 Newsgroups: perl.cpan.testers
150 Path: nntp.perl.org
151 Date: Fri, 1 Dec 2006 09:27:56 +0000
152 Subject: PASS POE-Component-IRC-5.14 cygwin-thread-multi-64int 1.5.21(0.15642)
153 From: chris@bingosnet.co.uk
154 Message-ID: <perl.cpan.testers-381062@nntp.perl.org>
155
156 This distribution has been tested as part of the cpan-testers
157 effort to test as many new uploads to CPAN as possible. See
158 http://testers.cpan.org/
159
160DESCRIPTION
161
162 POE::Component::Server::NNTP is a POE component that implements an RFC
163 977 http://www.faqs.org/rfcs/rfc977.html NNTP server. It is the
164 companion component to POE::Component::Client::NNTP which implements
165 NNTP client functionality.
166
167 You spawn an NNTP server component, create your POE sessions then
168 register your session to receive events. Whenever clients connect,
169 disconnect or send valid NNTP protocol commands you will receive an
170 event and an unique client ID. You then parse and process the commands
171 given and send back applicable NNTP responses.
172
173 This component doesn't implement the news database and as such is not
174 by itself a complete NNTP daemon implementation.
175
176CONSTRUCTOR
177
178 spawn
179
180 Takes a number of optional arguments:
181
182 'alias', set an alias on the component;
183 'address', bind the component to a particular address, defaults to INADDR_ANY;
184 'port', start the listening server on a different port, defaults to 119;
185 'options', a hashref of POE::Session options;
186 'posting', a true or false value that determines whether the poco
187 responds with a 200 or 201 to clients;
188 'handle_connects', true or false whether the poco sends 200/201
189 responses to connecting clients automagically;
190 'extra_cmds', an arrayref of additional NNTP commands that you
191 wish to implement.
192
193 Returns a POE::Component::Server::NNTP object.
194
195METHODS
196
197 session_id
198
199 Returns the POE::Session ID of the component.
200
201 shutdown
202
203 Terminates the component. Shuts down the listener and disconnects
204 connected clients.
205
206 send_event
207
208 Sends an event through the component's event handling system.
209
210 send_to_client
211
212 Send some output to a connected client. First parameter must be a
213 valid client id. Second parameter is a string of text to send.
214
215INPUT
216
217 These are events that the component will accept:
218
219 register
220
221 Takes N arguments: a list of event names that your session wants to
222 listen for, minus the 'nntpd_' prefix, ( this is similar to
223 POE::Component::IRC ).
224
225 Registering for 'all' will cause it to send all NNTPD-related events
226 to you; this is the easiest way to handle it.
227
228 unregister
229
230 Takes N arguments: a list of event names which you don't want to
231 receive. If you've previously done a 'register' for a particular
232 event which you no longer care about, this event will tell the NNTPD
233 to stop sending them to you. (If you haven't, it just ignores you. No
234 big deal).
235
236 shutdown
237
238 Terminates the component. Shuts down the listener and disconnects
239 connected clients.
240
241 send_event
242
243 Sends an event through the component's event handling system.
244
245 send_to_client
246
247 Send some output to a connected client. First parameter must be a
248 valid client ID. Second parameter is a string of text to send.
249
250OUTPUT
251
252 The component sends the following events to registered sessions:
253
254 nntpd_registered
255
256 This event is sent to a registering session. ARG0 is
257 POE::Component::Server::NNTP object.
258
259 nntpd_listener_failed
260
261 Generated if the component cannot either start a listener or there is
262 a problem accepting client connections. ARG0 contains the name of the
263 operation that failed. ARG1 and ARG2 hold numeric and string values
264 for $!, respectively.
265
266 nntpd_connection
267
268 Generated whenever a client connects to the component. ARG0 is the
269 client ID, ARG1 is the client's IP address, ARG2 is the client's TCP
270 port. ARG3 is our IP address and ARG4 is our socket port.
271
272 nntpd_disconnected
273
274 Generated whenever a client disconnects. ARG0 is the client ID.
275
276 nntpd_cmd_*
277
278 Generated for each NNTP command that a connected client sends to us.
279 ARG0 is the client ID. ARG1 .. ARGn are any parameters that are sent
280 with the command. Check the RFC http://www.faqs.org/rfcs/rfc977.html
281 for details.
282
283 nntpd_posting
284
285 When the component receives a posting from a client, either as the
286 result of a IHAVE or POST command, this event is issued. ARG0 will be
287 the client ID. ARG1 will be either a '335' or '340' indicating what
288 the posting relates to ( either an IHAVE or POST ). ARG2 will be an
289 arrayref containing the raw lines that the client sent us. No
290 additional parsing is undertaken on this data.
291
292PLUGINS
293
294 POE::Component::Server::NNTP utilises POE::Component::Pluggable to
295 enable a POE::Component::IRC type plugin system.
296
297 PLUGIN HANDLER TYPES
298
299 There are two types of handlers that can registered for by plugins,
300 these are
301
302 NNTPD
303
304 These are the 'nntpd_' prefixed events that are generated. In a
305 handler arguments are passed as scalar refs so that you may mangle
306 the values if required.
307
308 NNTPC
309
310 These are generated whenever a response is sent to a client. Again,
311 any arguments passed are scalar refs for manglement. There is really
312 on one type of this handler generated 'NNTPC_response'
313
314 PLUGIN EXIT CODES
315
316 Plugin handlers should return a particular value depending on what
317 action they wish to happen to the event. These values are available as
318 constants which you can use with the following line:
319
320 use POE::Component::Server::NNTP::Constants qw(:ALL);
321
322 The return values have the following significance:
323
324 NNTPD_EAT_NONE
325
326 This means the event will continue to be processed by remaining
327 plugins and finally, sent to interested sessions that registered for
328 it.
329
330 NNTP_EAT_CLIENT
331
332 This means the event will continue to be processed by remaining
333 plugins but it will not be sent to any sessions that registered for
334 it. This means nothing will be sent out on the wire if it was an
335 NNTPC event, beware!
336
337 NNTPD_EAT_PLUGIN
338
339 This means the event will not be processed by remaining plugins, it
340 will go straight to interested sessions.
341
342 NNTPD_EAT_ALL
343
344 This means the event will be completely discarded, no plugin or
345 session will see it. This means nothing will be sent out on the wire
346 if it was an NNTPC event, beware!
347
348 PLUGIN METHODS
349
350 The following methods are available:
351
352 pipeline
353
354 Returns the POE::Component::Pluggable::Pipeline object.
355
356 plugin_add
357
358 Accepts two arguments:
359
360 The alias for the plugin
361 The actual plugin object
362
363 The alias is there for the user to refer to it, as it is possible to
364 have multiple plugins of the same kind active in one
365 POE::Component::Server::NNTP object.
366
367 This method goes through the pipeline's push() method.
368
369 This method will call $plugin->plugin_register( $nntpd )
370
371 Returns the number of plugins now in the pipeline if plugin was
372 initialized, undef if not.
373
374 plugin_del
375
376 Accepts one argument:
377
378 The alias for the plugin or the plugin object itself
379
380 This method goes through the pipeline's remove() method.
381
382 This method will call $plugin->plugin_unregister( $nntpd )
383
384 Returns the plugin object if the plugin was removed, undef if not.
385
386 plugin_get
387
388 Accepts one argument:
389
390 The alias for the plugin
391
392 This method goes through the pipeline's get() method.
393
394 Returns the plugin object if it was found, undef if not.
395
396 plugin_list
397
398 Has no arguments.
399
400 Returns a hashref of plugin objects, keyed on alias, or an empty list
401 if there are no plugins loaded.
402
403 plugin_order
404
405 Has no arguments.
406
407 Returns an arrayref of plugin objects, in the order which they are
408 encountered in the pipeline.
409
410 plugin_register
411
412 Accepts the following arguments:
413
414 The plugin object
415 The type of the hook, NNTPD or NNTPC
416 The event name(s) to watch
417
418 The event names can be as many as possible, or an arrayref. They
419 correspond to the prefixed events and naturally, arbitrary events
420 too.
421
422 You do not need to supply events with the prefix in front of them,
423 just the names.
424
425 It is possible to register for all events by specifying 'all' as an
426 event.
427
428 Returns 1 if everything checked out fine, undef if something's
429 seriously wrong
430
431 plugin_unregister
432
433 Accepts the following arguments:
434
435 The plugin object
436 The type of the hook, NNTPD or NNTPC
437 The event name(s) to unwatch
438
439 The event names can be as many as possible, or an arrayref. They
440 correspond to the prefixed events and naturally, arbitrary events
441 too.
442
443 You do not need to supply events with the prefix in front of them,
444 just the names.
445
446 It is possible to register for all events by specifying 'all' as an
447 event.
448
449 Returns 1 if all the event name(s) was unregistered, undef if some
450 was not found.
451
452 PLUGIN TEMPLATE
453
454 The basic anatomy of a plugin is:
455
456 package Plugin;
457
458 # Import the constants, of course you could provide your own
459 # constants as long as they map correctly.
460 use POE::Component::Server::NNTP::Constants qw( :ALL );
461
462 # Our constructor
463 sub new {
464 ...
465 }
466
467 # Required entry point for plugins
468 sub plugin_register {
469 my( $self, $nntpd ) = @_;
470
471 # Register events we are interested in
472 $nntpd->plugin_register( $self, 'NNTPD', qw(all) );
473
474 # Return success
475 return 1;
476 }
477
478 # Required exit point for pluggable
479 sub plugin_unregister {
480 my( $self, $nntpd ) = @_;
481
482 # Pluggable will automatically unregister events for the plugin
483
484 # Do some cleanup...
485
486 # Return success
487 return 1;
488 }
489
490 sub _default {
491 my( $self, $nntpd, $event ) = splice @_, 0, 3;
492
493 print "Default called for $event\n";
494
495 # Return an exit code
496 return NNTPD_EAT_NONE;
497 }
498
499SEE ALSO
500
501 POE::Component::Client::NNTP
502
503 RFC 977 http://www.faqs.org/rfcs/rfc977.html
504
505 RFC 1036 http://www.faqs.org/rfcs/rfc1036.html
506
507 POE::Component::Pluggable
508
509AUTHOR
510
511 Chris Williams <chris@bingosnet.co.uk>
512
513COPYRIGHT AND LICENSE
514
515 This software is copyright (c) 2016 by Chris Williams.
516
517 This is free software; you can redistribute it and/or modify it under
518 the same terms as the Perl 5 programming language system itself.
519
520