#usenet #api-bindings #group #tls #exit #authentication #body #article #head #newgroups

rek2_nntp

This is a Rust library that provides a way to interact with NNTP servers, compliant with RFC 3977 and RFC 4643

13 releases (6 stable)

Uses new Rust 2024

new 1.0.5 Apr 24, 2025
1.0.4 Apr 23, 2025
0.1.6 Apr 15, 2025
0.1.5 Oct 1, 2023
0.1.1 Sep 26, 2023

#367 in Network programming

Download history 292/week @ 2025-04-14 539/week @ 2025-04-21

831 downloads per month
Used in usenet_reborn

GPL-3.0-or-later

35KB
571 lines

ReK2 NNTP RFC3977/RFC4643 Rust Library

  • By Chris F.N. aka ReK2

builds.sr.ht status

rek2_nntp is a Rust asynchronous NNTP client library compliant with RFC 3977 and RFC 4643. It allows you to connect to NNTP servers to read, list, and post articles securely via TLS/SSL.

Features

  • ✅ Authentication (with TLS/SSL)
  • ✅ Listing available newsgroups (LIST)
  • ✅ Reading articles (ARTICLE)
  • ✅ Posting articles (POST)
  • ✅ Selecting newsgroups (GROUP)
  • ✅ Retrieving new newsgroups (NEWGROUPS)
  • ✅ Retrieving article headers (HEAD)
  • ✅ Retrieving article bodies (BODY)
  • ✅ Retrieving article statistics (STAT)
  • ✅ Graceful session termination (QUIT)
  • ✅ Fast article header fetching via (XOVER)
  • ⚠️ Retrieving new articles (NEWNEWS) (often disabled on modern servers, use with caution)

Installation

Add the following to your project's Cargo.toml:

[dependencies]
rek2_nntp = "1.0.0"  # Replace with the latest version

Then, run:

cargo build

Usage

Authentication

use rek2_nntp::authenticate;

#[tokio::main]
async fn main() {
    let result = authenticate("news.example.com", "username", "password").await;
    match result {
        Ok(mut connection) => {
            println!("Successfully authenticated!");
            // You can now use `connection` with other commands
        }
        Err(err) => {
            eprintln!("Failed to authenticate: {}", err);
        }
    }
}

Listing Newsgroups

use rek2_nntp::{authenticate, list_newsgroups};

let mut connection = authenticate("news.example.com", "username", "password").await?;

let newsgroups = list_newsgroups(&mut connection).await?;
for group in newsgroups.iter().take(10) {
    println!("{} ({}-{}) Status: {}", group.name, group.low, group.high, group.status);
}

Reading Articles from a Newsgroup

use rek2_nntp::{authenticate, read_from_group};

let mut connection = authenticate("news.example.com", "username", "password").await?;

// Read first 5 articles from "comp.lang.rust"
let articles = read_from_group(&mut connection, "comp.lang.rust", Some((1, 5))).await?;
for article in articles {
    println!("Header: {}\nBody: {}", article.header, article.body);
}

Posting an Article

use rek2_nntp::{authenticate, post_to_group, PostArticle};

let mut connection = authenticate("news.example.com", "username", "password").await?;

let article = PostArticle {
    from: "user@example.com".to_string(),
    newsgroups: "comp.lang.rust".to_string(),
    subject: "Hello Rust!".to_string(),
    body: "This is a test article posted using rek2_nntp".to_string(),
};

post_to_group(&mut connection, &article, &"comp.lang.rust".to_string()).await?;
println!("Article posted successfully!");

Selecting a Newsgroup (GROUP)

use rek2_nntp::{authenticate, group};

let mut connection = authenticate("news.example.com", "username", "password").await?;

let response = group(&mut connection, "comp.lang.c").await?;
println!("Server Response: {}", response);

Retrieving New Newsgroups (NEWGROUPS)

use rek2_nntp::{authenticate, newgroups};

let mut connection = authenticate("news.example.com", "username", "password").await?;

let groups = newgroups(&mut connection, "20250101", "000000", None).await?;
for group in groups.iter().take(5) {
    println!("Newsgroup: {}", group.name);
}

Retrieving Article Headers (HEAD)

use rek2_nntp::{authenticate, head, group};

let mut connection = authenticate("news.example.com", "username", "password").await?;
let _ = group(&mut connection, "comp.lang.c").await?;

let header = head(&mut connection, "1").await?;
println!("Header: {}", header);

Retrieving Article Bodies (BODY)

use rek2_nntp::{authenticate, body, group};

let mut connection = authenticate("news.example.com", "username", "password").await?;
let _ = group(&mut connection, "comp.lang.c").await?;

let article_body = body(&mut connection, "1").await?;
println!("Body: {}", article_body);

Retrieving Article Statistics (STAT)

use rek2_nntp::{authenticate, stat, group};

let mut connection = authenticate("news.example.com", "username", "password").await?;
let _ = group(&mut connection, "comp.lang.c").await?;

let message_id = stat(&mut connection, "1").await?;
println!("Message ID: {}", message_id);

Fetch overview headers for a range of articles in a newsgroup (XOVER)

let articles = fetch_xover_range(&mut conn, "alt.test", Some((1, 50))).await?;
for art in articles {
    println!("{}{}", art.article_id, art.subject);
}

Gracefully Ending the Session (QUIT)

use rek2_nntp::{authenticate, quit};

let mut connection = authenticate("news.example.com", "username", "password").await?;
quit(&mut connection).await?;
println!("Session closed.");

Running Integration Tests

Set environment variables before running tests:

export NNTP_HOST=news.example.com
export NNTP_USERNAME=username
export NNTP_PASSWORD=password

Then run:

cargo test -- --nocapture

Contributing

Contributions are welcome!

License

This library is licensed under the GNU GPL v3.

Dependencies

~12–23MB
~413K SLoC