Actually include source files, whoops!

[?]
Jan 4, 2021, 6:52 AM
JFL253ZZDBEQ4SAYXRI5LIHTU5IH6Z7UUMOJTKLNMY3VU7KATH2QC

Dependencies

Change contents

  • file addition: src (dxwrx-rx-r)
    [2.10]
  • file addition: main.rs (-xw-x--x--)
    [0.6]
    use std::io::{stdin, Read, Write};
    use anyhow::{anyhow, Result};
    use libtls::{config::Builder, tls::Tls};
    use pico_args::Arguments;
    use url::{Position, Url};
    #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
    enum Status {
    Input,
    Success,
    Redirect,
    TemporaryFailure,
    PermanentFailure,
    ClientCertificateRequired,
    }
    #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
    struct Header {
    status: u8,
    meta: String,
    }
    impl Header {
    pub fn status(&self) -> Status {
    if self.status >= 60 {
    Status::ClientCertificateRequired
    } else if self.status >= 50 {
    Status::PermanentFailure
    } else if self.status >= 40 {
    Status::TemporaryFailure
    } else if self.status >= 30 {
    Status::Redirect
    } else if self.status >= 20 {
    Status::Success
    } else if self.status >= 10 {
    Status::Input
    } else {
    panic!("invalid status: {}", self.status)
    }
    }
    }
    fn parse_address(mut address: String) -> Result<Url> {
    if !address.trim_start().starts_with("gemini://") {
    if address.contains("://") {
    return Err(anyhow!("only gemini is supported"));
    }
    address = format!("gemini://{}", address);
    }
    let mut url = Url::parse(address.as_str())?;
    if !url.username().is_empty() || url.password().is_some() {
    return Err(anyhow!("no user info is allowed"));
    }
    if !url.has_host() {
    return Err(anyhow!("must supply host"));
    }
    if url.port().is_none() {
    url.set_port(Some(1965)).expect("port error")
    }
    Ok(url)
    }
    fn make_request(url: &Url) -> Result<(Header, String)> {
    let mut client = Tls::client()?;
    let config = Builder::new().noverifycert().build()?;
    client.configure(&config)?;
    // Open Connection
    client.connect(&url[Position::BeforeHost..Position::AfterPort], None)?;
    // Complete TLS handshake
    let shaken = client.tls_handshake()?;
    if shaken != 0 {
    return Err(anyhow!("tls handshake failed"));
    }
    // Validate Server Certificate
    // per section 4.2, we opt not to validate TLS connections beyond the handshake
    // Send request
    let req = format!("{}\r\n", url);
    let written = client.write(req.as_bytes())?;
    if written == 0 {
    return Err(anyhow!("failed to write request"));
    } else if written != req.len() {
    eprintln!(
    "warning: request was {} bytes, only wrote {}",
    req.len(),
    written
    )
    }
    // Handle response
    let mut res = String::new();
    let read = client.read_to_string(&mut res)?;
    if read == 0 {
    return Err(anyhow!("failed to read response"));
    }
    let line_break = res
    .match_indices("\r\n")
    .next()
    .expect("response missing CRLF")
    .0;
    // 2 status + 1 space + 1024 meta
    if line_break >= 1028 {
    return Err(anyhow!("meta was too long"));
    }
    let body = res.split_off(line_break);
    let header = Header {
    status: (&res[..2]).parse().unwrap(),
    meta: (&res[2..]).to_string(),
    };
    Ok((header, body))
    }
    fn handle_response(url: Url, header: Header, body: String) -> Result<()> {
    println!("status: {}", header.status);
    Ok(match header.status() {
    Status::Input => {
    println!("server is requesting input");
    println!("{}", header.meta);
    print!("> ");
    let mut line = String::from("?");
    stdin().read_line(&mut line)?;
    let with_input = url.join(line.as_str())?;
    println!("connecting to {} again with new input", with_input);
    let (header, body) = make_request(&with_input)?;
    handle_response(with_input, header, body)?
    }
    Status::Success => {
    // ignoring meta
    println!("{}", body)
    }
    Status::Redirect => {
    let address = header.meta;
    let redirect = parse_address(address)?;
    println!("redirecting {} to {}", url, redirect);
    let (header, body) = make_request(&redirect)?;
    handle_response(redirect, header, body)?
    }
    Status::TemporaryFailure | Status::PermanentFailure => {
    eprintln!("error from server");
    eprintln!("{}", header.meta)
    }
    Status::ClientCertificateRequired => {
    eprintln!("server requires a client certificate");
    eprintln!("{}", header.meta)
    }
    })
    }
    fn main() -> Result<()> {
    let mut args = Arguments::from_env();
    let address: String = args.free_from_str()?.expect("must supply address");
    let url = parse_address(address)?;
    println!("connecting to {}", url);
    let (header, body) = make_request(&url)?;
    handle_response(url, header, body)
    }
  • file addition: Cargo.toml (-xw-x--x--)
    [2.10]
    [package]
    authors = ["Matthew Ess <daringseal@gmail.com>"]
    edition = "2018"
    name = "sputnik"
    version = "0.1.0"
    # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
    [dependencies]
    anyhow = "1.0.37"
    libtls = "1.2.0"
    pico-args = "0.3.4"
    url = "2.2.0"
  • file addition: sources.nix (-xw-x--x--)
    [2.438]
    # This file has been generated by Niv.
    let
    #
    # The fetchers. fetch_<type> fetches specs of type <type>.
    #
    fetch_file = pkgs: spec:
    if spec.builtin or true then
    builtins_fetchurl { inherit (spec) url sha256; }
    else
    pkgs.fetchurl { inherit (spec) url sha256; };
    fetch_tarball = pkgs: name: spec:
    let
    ok = str: !builtins.isNull (builtins.match "[a-zA-Z0-9+-._?=]" str);
    # sanitize the name, though nix will still fail if name starts with period
    name' = stringAsChars (x: if !ok x then "-" else x) "${name}-src";
    in if spec.builtin or true then
    builtins_fetchTarball {
    name = name';
    inherit (spec) url sha256;
    }
    else
    pkgs.fetchzip {
    name = name';
    inherit (spec) url sha256;
    };
    fetch_git = spec:
    builtins.fetchGit {
    url = spec.repo;
    inherit (spec) rev ref;
    };
    fetch_local = spec: spec.path;
    fetch_builtin-tarball = name:
    throw ''
    [${name}] The niv type "builtin-tarball" is deprecated. You should instead use `builtin = true`.
    $ niv modify ${name} -a type=tarball -a builtin=true'';
    fetch_builtin-url = name:
    throw ''
    [${name}] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`.
    $ niv modify ${name} -a type=file -a builtin=true'';
    #
    # Various helpers
    #
    # The set of packages used when specs are fetched using non-builtins.
    mkPkgs = sources:
    let
    sourcesNixpkgs =
    import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; })
    { };
    hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath;
    hasThisAsNixpkgsPath = <nixpkgs> == ./.;
    in if builtins.hasAttr "nixpkgs" sources then
    sourcesNixpkgs
    else if hasNixpkgsPath && !hasThisAsNixpkgsPath then
    import <nixpkgs> { }
    else
    abort ''
    Please specify either <nixpkgs> (through -I or NIX_PATH=nixpkgs=...) or
    add a package called "nixpkgs" to your sources.json.
    '';
    # The actual fetching function.
    fetch = pkgs: name: spec:
    if !builtins.hasAttr "type" spec then
    abort "ERROR: niv spec ${name} does not have a 'type' attribute"
    else if spec.type == "file" then
    fetch_file pkgs spec
    else if spec.type == "tarball" then
    fetch_tarball pkgs name spec
    else if spec.type == "git" then
    fetch_git spec
    else if spec.type == "local" then
    fetch_local spec
    else if spec.type == "builtin-tarball" then
    fetch_builtin-tarball name
    else if spec.type == "builtin-url" then
    fetch_builtin-url name
    else
    abort
    "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}";
    # Ports of functions for older nix versions
    # a Nix version of mapAttrs if the built-in doesn't exist
    mapAttrs = builtins.mapAttrs or (f: set:
    with builtins;
    listToAttrs (map (attr: {
    name = attr;
    value = f attr set.${attr};
    }) (attrNames set)));
    # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295
    range = first: last:
    if first > last then
    [ ]
    else
    builtins.genList (n: first + n) (last - first + 1);
    # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257
    stringToCharacters = s:
    map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1));
    # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269
    stringAsChars = f: s: concatStrings (map f (stringToCharacters s));
    concatStrings = builtins.concatStringsSep "";
    # fetchTarball version that is compatible between all the versions of Nix
    builtins_fetchTarball = { url, name, sha256 }@attrs:
    let inherit (builtins) lessThan nixVersion fetchTarball;
    in if lessThan nixVersion "1.12" then
    fetchTarball { inherit name url; }
    else
    fetchTarball attrs;
    # fetchurl version that is compatible between all the versions of Nix
    builtins_fetchurl = { url, sha256 }@attrs:
    let inherit (builtins) lessThan nixVersion fetchurl;
    in if lessThan nixVersion "1.12" then
    fetchurl { inherit url; }
    else
    fetchurl attrs;
    # Create the final "sources" from the config
    mkSources = config:
    mapAttrs (name: spec:
    if builtins.hasAttr "outPath" spec then
    abort
    "The values in sources.json should not have an 'outPath' attribute"
    else
    spec // { outPath = fetch config.pkgs name spec; }) config.sources;
    # The "config" used by the fetchers
    mkConfig = { sourcesFile ? ./sources.json
    , sources ? builtins.fromJSON (builtins.readFile sourcesFile)
    , pkgs ? mkPkgs sources }: rec {
    # The sources, i.e. the attribute set of spec name to spec
    inherit sources;
    # The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers
    inherit pkgs;
    };
    in mkSources (mkConfig { }) // {
    __functor = _: settings: mkSources (mkConfig settings);
    }
  • file addition: sources.json (-xw-x--x--)
    [2.438]
    {
    "niv": {
    "branch": "master",
    "description": "Easy dependency management for Nix projects",
    "homepage": "https://github.com/nmattia/niv",
    "owner": "nmattia",
    "repo": "niv",
    "rev": "ba57d5a29b4e0f2085917010380ef3ddc3cf380f",
    "sha256": "1kpsvc53x821cmjg1khvp1nz7906gczq8mp83664cr15h94sh8i4",
    "type": "tarball",
    "url": "https://github.com/nmattia/niv/archive/ba57d5a29b4e0f2085917010380ef3ddc3cf380f.tar.gz",
    "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
    },
    "nixpkgs": {
    "branch": "nixos-unstable",
    "description": "Nix Packages collection",
    "homepage": null,
    "owner": "NixOS",
    "repo": "nixpkgs",
    "rev": "a371c1071161104d329f6a85d922fd92b7cbab63",
    "sha256": "1k5wa16wyb1byk5xfjlq4m518gsw6g1kypx4xb09k3inni13p0r4",
    "type": "tarball",
    "url": "https://github.com/NixOS/nixpkgs/archive/a371c1071161104d329f6a85d922fd92b7cbab63.tar.gz",
    "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
    },
    "nixpkgs-mozilla": {
    "branch": "master",
    "description": "mozilla related nixpkgs (extends nixos/nixpkgs repo)",
    "homepage": null,
    "owner": "mozilla",
    "repo": "nixpkgs-mozilla",
    "rev": "8c007b60731c07dd7a052cce508de3bb1ae849b4",
    "sha256": "1zybp62zz0h077zm2zmqs2wcg3whg6jqaah9hcl1gv4x8af4zhs6",
    "type": "tarball",
    "url": "https://github.com/mozilla/nixpkgs-mozilla/archive/8c007b60731c07dd7a052cce508de3bb1ae849b4.tar.gz",
    "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
    }
    }
  • file addition: default.nix (-xw-x--x--)
    [2.438]
    let
    sources = import ./sources.nix;
    mozilla = import sources.nixpkgs-mozilla;
    nixpkgs = import sources.nixpkgs;
    in nixpkgs { overlays = [ mozilla ]; }