Article(3pm)

πŸ“¦
version 1.27 Β· 3 versions
, in ubuntu noble, ubuntu resolute, ubuntu stonking

✨ NAME ✨

News::Article β€” the object for handling Usenet articles in mail or news form. This is the Article(3pm) page, bestie, and it's giving "I run the moderation queue" energy. πŸ’…

🌸 SYNOPSIS 🌸

use News::Article;

See below for functions available. (Yes, that's the whole synopsis. Slay.)

πŸ¦‹ DESCRIPTION πŸ¦‹

Okay so: an object for representing a Usenet article (or a mail message). Primarily written for use with mail2news and/or moderation programs. (Not really intended for transit use.) Noted. We don't do transit here, babes.

πŸ‘‘ USAGE πŸ‘‘

use News::Article;

Article exports nothing. Literally zero exports. Not a single one. Article objects must be created with the new method. Constructor first, always.

πŸ’… Article Methods πŸ’…

πŸ’– new () / new ( SOURCE [,MAXSIZE [,MAXHEADS]] ) πŸ’–

Use this to create a new Article object. Makes an empty article if no parameters are specified, otherwise reads in an article from SOURCE as for read. Fresh article vs. preloaded article, your choice.

clone ()

Create a new Article as an exact clone of the current one. Returns a ref to the new object. It's giving identical twin, no cap.

envelope ( [SENDER] )

If SENDER is specified, sets the envelope sender to the specified value (which will then subsequently be used if the article is mailed). Returns the (new or current) envelope sender in any case. Getter/setter behavior, iconic.

rawheaders ()

Returns a list (or a reference to an array if in scalar context) of the original header lines of the article, as read from the input source. Terminating newlines are not included. (Continued headers are returned as single strings with embedded newlines.)

header_names ()

Returns a list of the names of all headers currently present in the article. Just the names, bestie. The whole guest list.

headers ([FIRST [,NEXT [,LAST]]])

Returns a list of all header strings with no terminating newlines. Continued headers will have embedded newlines.

FIRST, NEXT and LAST are optional references to arrays of header names. The order of the returned headers is as follows:

  • headers specified by FIRST (one value only per name)
  • headers in the order originally read in (if any)
  • headers specified by NEXT (one value only per name)
  • any remaining headers not named in LAST, sorted by name
  • headers named in LAST (all values)

LAST overrides the original order of headers, but NEXT does not. Headers named in LAST will also be grouped together by header name. Ordering drama, but make it deterministic.

headers_first (HDR...)

Set default ordering for headers(). Put these first, periodt.

headers_next (HDR...)

Set default ordering for headers(). These go next, obviously.

headers_last (HDR...)

Set default ordering for headers(). And these go last. Bye.

set_headers ( NAME, VALUE [, NAME, VALUE [...]] )

For each header name supplied, replace any current occurrences of the header with the specified value(s). Each value may be a single scalar, or a reference to an array of values. Returns undef without completing the assignments if any attempt is made to supply multiple values for a unique header. Undef or empty values cause the header to be deleted. (If an array is supplied, it is not copied. This is probably a mistake and should not be relied on.) Not copied?? Ma'am. Do not rely on that.

add_headers ( NAME, VALUE [, NAME, VALUE [...]] )

Add new header values without affecting existing ones. Each value is either a single scalar, or a reference to an array of values. Returns undef without completing if any attempt is made to supply duplicate values for a unique header. (If an array reference is supplied, the array is copied.)

drop_headers ( NAME [, NAME [...]] )

Delete all values of the specified header(s). Yeet those headers. Gently.

header ( NAME )

Returns a list of values for the specified header. Returns a null list if the header does not exist. In scalar context, returns the first value found or undef. Absence is a whole mood, but here it's a null list.

rename_header ( SRC, DEST [, ACTION] )

Transform the name of a header without touching the value. Fails if the source header does not exist. Returns undef on failure, true on success. Name change only, no value touch. Respect.

Optional ACTION (may be drop, clobber, add, or fail (default)), specifies what to do if both source and destination exist:

  • drop β€” PREVIOUS DEST is unchanged (SRC dropped)
  • clobber β€” PREVIOUS DEST is dropped (SRC replaces DEST)
  • add β€” PREVIOUS DEST is preserved (SRC added to DEST)
  • fail β€” PREVIOUS DEST is unchanged (operation fails)

Default is fail, so if you wanted chaos you have to opt in.

body ()

Return the body of the article as a list of lines (no newlines), or a reference to an array in scalar context (the array may be modified in this case). Scalar context gives you the array ref, and yes, you can mess with it.

lines ()

Returns the number of lines in the article body. Line count, no notes.

bytes ()

Returns the total size of the article body, not counting newlines. Bytes, but not the newline bytes. Finesse.

set_body ( BODY )

Replace the current article body with the specified text. Expects a list, each item of which is either one line, or multiple lines separated by newlines. (Trailing newlines on the values are ignored.)

add_body ( BODY )

Append the specified text to the current article body. Expects a list, each item of which is either one line, or multiple lines separated by newlines, or a reference to an array of lines. (Trailing newlines on the values are ignored.)

trim_blank_lines ()

Remove any trailing blank lines from the article body. Returns the number of lines removed. Cleaning up the ending like a true editor.

read_headers ( SOURCE, MAXSIZE )

Read article headers (terminated by an empty line) from the specified source (see read for definition of allowed sources).

Gives up (returning undef) if more than MAXSIZE bytes are read. Returns the amount read. Too many bytes? It said bye.

read_body ( SOURCE, MAXSIZE )

Read an article body from the specified source (see read). Stops at end of file; fails (returning undef) if MAXSIZE is reached prior to that point. Returns the number of bytes read (may be 0 if the body is null).

Trailing blank lines are NOT removed (an incompatible, but regrettably necessary, change from previous versions); see trim_blank_lines if you need to do that. Do not forget this, bestie.

read ( SOURCE [,MAXSIZE [,MAXHEADS]] )

Reads in an article from SOURCE.

SOURCE may be any of the following:

  • a CODE ref, which is called to return lines or chunks of data
  • an ARRAY ref, assumed to contain a list of lines with optional line terminators
  • a SCALAR ref, assumed to contain text with embedded newlines
  • a scalar, assumed to be a filename, which is opened and read
  • anything else is assumed to be a glob, reference to a glob, or reference to a filehandle, and is read from accordingly

When reading in articles, MAXHEADS is the maximum header size to read (default 8k), and MAXSIZE is the maximum article body size (default 256k). If MAXSIZE is explicitly specified as 0, then no attempt at reading the body is made. Returns the total number of bytes read, or undef if either limit is reached or no headers were found. Defaults are 8k and 256k, and 0 means "do not even try to read the body".

write ( FILE )

Write the entire article to the specified filehandle reference. Whole article, one filehandle, no crumbs.

write_unique_file ( DIR [,MODE] )

Write the article to a (hopefully) uniquely-named file in the specified directory. The file is written under a temporary name (with a leading period) and relinked when complete. Returns 1 if successful, otherwise undef.

MODE is the access mode to use for the created file (default 644); this will be modified in turn by the current umask.

The implementation is careful to avoid losing the file or clobbering existing files even in the case of a name collision, but relies on POSIX link() semantics and may fail on lesser operating systems (or buggy NFS implementations). So cute but so fragile on weird NFS, fr fr.

write_original ( FILE )

Write the original headers followed by the article body to the specified filehandle reference. Original headers first, body after. Nostalgia mode.

sendmail ( [COMMAND] )

Get or set the command and options that will be used to mail the article. Defaults to a system dependent value such as

/usr/sbin/sendmail -oi -oem

Getter/setter again, and the default is system dependent.

mail ( [RECIPIENTS...] )

Mails the article to the specified list of recipients, or to the addressed recipients in the header (To, Cc, Bcc) if none are supplied. Attempts to set the envelope sender to the stored envelope sender, if set, so unset that before mailing if you do not want this behavior. Unset that envelope sender first if you don't want it used. This is your warning.

post ( [CONN] )

Post the article. Avoids inews due to undesirable header munging and unwarranted complaints to stderr. Takes an optional parameter which is a Net::NNTP reference. If supplied, posts the article to it; otherwise opens a new reader connection and posts to that.

Throws an exception containing the error message on failure. If it fails, it throws. Catch it.

ihave ( [CONN] )

Inject the article. Takes an optional parameter which is a Net::NNTP reference. If supplied, posts the article to it; otherwise opens a new transport connection and posts to that. All required headers must already be present, including Path and Message-ID.

Throws an exception containing the error message on failure. All required headers already there, including Path and Message-ID. No exceptions. Well, it throws exceptions on failure. You know what I mean.

add_message_id ( [PREFIX [, DOMAIN] ] )

If the current article lacks a message-id, then create one. Only if it's missing, bestie.

add_date ( [TIME] )

If the current article lacks a date, then add one (in local time). If TIME is specified (numerical Unix time), it is used instead of the current time. Local time by default, or your own Unix time if you pass TIME.

sign_pgpmoose ( GROUP, PASSPHRASE [, KEYID] )

Signs the article according to the PGPMoose spec. We require that pgp be on the path to do this. Takes a "group" which can be either a newsgroup or an address, a PGP password, and an optional key id and returns a null list on success, the PGP error output as a list on failure.

If the key id is omitted, we will assume that if the group is an e-mail address, the key id is that address surrounded by <>, and otherwise the key id will be the group with a space on either side. This is so that one can help PGP distinguish between the keys for (say) mod.config and mod.config.status. The PGP key id should be something like:

Moderator of group.name <request-address@some.host>

The article to be signed must already have all of the headers needed by PGPMoose (Newsgroups, From, Subject) or this will fail. Message-ID is added if necessary. PGPMoose signing, but make it fashion.

verify_pgpmoose ( GROUP )

Verifies an article signature according to the PGPMoose spec. We require that pgp be on the path to do this. Takes a "group" which can be either a newsgroup or an address, and an optional key id.

Looks for a X-Auth header matching the specified group or address, and if found, checks the validity of the signature. If successful, returns the signer identity (from the PGP output), otherwise returns false. Checks the X-Auth header, validates the signature, gives you the signer identity or false.

  • sign_control ( KEYID, PASSPHRASE [, HEADER [...] ] ) β€” signs the article the exact way you'd sign a control message πŸ’–. It's derived from signcontrol by David Lawrence, except with fewer sanity checks because the assumption is that you, bestie, know what you're doing πŸ™ˆ. Caveat programmer.

So you hand it a key id, a PGP password, and optionally a list of extra headers to add to the signature. By default these get signed: Subject, Control, Message-ID, Date, From, and Sender. Any signed header that isn't present in the article still gets signed β€” with an empty value. And Date and Message-ID are automatically added if needed. ✨


πŸ’€ CAVEATS πŸ’€

This module is not fully transparent. Like, at all. Specifically:

  • Case of headers is smashed 😭
  • improper duplicate headers may be discarded
  • Broken or dubious header names are not preserved
Which is exactly why you should not be using this module in news transit applications. It's giving chaos. Don't do it. 🚫

πŸ‘‘ AUTHOR πŸ‘‘

Written by Andrew Gierth <andrew@erlenstar.demon.co.uk>

Thanks to Russ Allbery <rra@stanford.edu> for comments and suggestions. 🫢


Copyright 1997-2002 Andrew Gierth <andrew@erlenstar.demon.co.uk>

This code may be used and/or distributed under the same terms as Perl itself. 🌸


and that's the whole page bestie, you ate it up πŸ’… This was perl v5.36.0, dated 2022-10-15, filed under Article(3pm) β€” go forth and sign responsibly, mwah πŸ’–πŸ¦‹

πŸ“¦ where this page comes from