pub use std::fs::File;
pub use std::fs;
pub use std::hash::Hash;
+pub use std::io;
pub use std::io::ErrorKind;
pub use std::io::{BufReader, Read, BufRead, BufWriter, Write};
-pub use std::io;
+pub use std::io::{SeekFrom};
pub use std::iter::repeat_with;
pub use std::iter;
pub use std::marker::PhantomData;
pub use std::os::unix::ffi::OsStrExt;
pub use std::os::unix;
pub use std::path::PathBuf;
-pub use std::process::exit;
+pub use std::process::{exit, Command};
pub use std::str::FromStr;
pub use std::str;
pub use std::string::ParseError;
pub use crate::global::*;
pub use crate::keydata::*;
pub use crate::mgmtchannel::*;
+pub use crate::nwtemplates;
pub use crate::pieces::*;
pub use crate::shapelib;
pub use crate::slotmap_slot_idx::*;
// SPDX-License-Identifier: AGPL-3.0-or-later
// There is NO WARRANTY.
-use parking_lot::RwLock;
+use crate::imports::*;
-static STATE : RwLock<Option<State>> = const_mutex(None);
+use parking_lot::{const_rwlock, RwLock, MappedRwLockReadGuard};
+
+static STATE : RwLock<Option<State>> = const_rwlock(None);
+
+struct State {
+ tera: tera::Tera,
+}
#[throws(StartupError)]
pub fn init() {
assert!(guard.is_none());
let glob = format!("{}/*.tera", config().nwtemplates);
*guard = State {
- tera: tera::new(&glob)?,
+ tera: tera::Tera::new(&glob)?,
};
}
#[throws(tera::Error)]
-pub fn template_render<D: Serialize>(template_name: &str, data: &D) {
- fn get_st() -> MappedRwLockReadGuard<State> {
+pub fn render<D: Serialize>(template_name: &str, data: &D) {
+ fn get_st() -> MappedRwLockReadGuard<'static, State> {
STATE.read().as_ref().unwrap()
}
get_st().render(template_name, data)
#[derive(Debug,Serialize,Deserialize)]
pub struct TokenByEmail {
/// RFC822 recipient field syntax (therefore, ASCII)
- pub addr: Sring,
+ pub addr: String,
}
#[derive(Debug,Serialize,Deserialize)]
fn check_spec_permission(&self, _: Option<AuthorisationSuperuser>) {
}
fn deliver(&self,
+ g: &Instance,
gpl: &GPlayerState,
ipl: &IPlayerState,
token: AccessTokenInfo)
impl PlayerAccessSpec for PlayerAccessUnset {
#[throws(TokenDeliveryError)]
fn deliver(&self,
+ _g: &Instance,
_gpl: &GPlayerState,
_ipl: &IPlayerState,
_token: AccessTokenInfo) -> AccessTokenReport {
}
#[throws(TokenDeliveryError)]
fn deliver(&self,
+ _g: &Instance,
_gpl: &GPlayerState,
_ipl: &IPlayerState,
_token: AccessTokenInfo) -> AccessTokenReport {
impl PlayerAccessSpec for UrlOnStdout {
#[throws(TDE)]
fn deliver<'t>(&self,
+ _g: &Instance,
_gpl: &GPlayerState,
_ipl: &IPlayerState,
token: AccessTokenInfo)
ipl: &IPlayerState,
token: AccessTokenInfo)
-> AccessTokenReport {
- let messagefile = tempfile::tempfile()?;
-
- #[derive(Serialize)]
+ #[derive(Debug,Serialize)]
struct CommonData<'r> {
player_email: &'r str,
- game_name: &'r str,
- token_url: &'r str,
+ game_name: String,
nick: &'r str,
+ token_lines: Vec<String>,
};
let common = CommonData {
player_email: &self.addr,
- game_name: &g.name,
- token_url: &
+ game_name: g.name.to_string(),
+ token_lines: token.report(),
+ nick: &gpl.nick,
+ };
+
+ if self.addr.find(['\r','\n'] as &[char]).is_some() {
+ throw!(anyhow!("email address may not contain line endings"));
}
let message = match &ipl.account {
AS::Unix { user } => {
- struct Data {
- pub gname:
- }
-
-
- write!(&mut message, r#"\
-"#,
- &self.addr, &gname,
- &ipl.account, &gname,
-
-
- write!(&mut message, r#"\
-! "#,
- &self.addr, &gname,
- &ipl.account, &gname,
- let command = Command::new(&config().sendmail)
- .args(&["-oee","-odb","-oi","--"])
- .stdin(
-
+ #[derive(Debug,Serialize)]
+ struct Data<'r> {
+ unix_user: &'r str,
+ #[serde(flatten)]
+ common: CommonData<'r>,
+ };
+ let data = Data {
+ unix_user: user,
+ common,
+ };
+ nwtemplates::render("token-unix", &data)
+ }
+ other => {
+ #[derive(Debug,Serialize)]
+ struct Data<'r> {
+ account: String,
+ #[serde(flatten)]
+ common: CommonData<'r>,
+ };
+ let data = Data {
+ account: other.to_string(),
+ common,
+ };
+ nwtemplates::render("token-other", &data)
+ }
+ }.context("render email template")?;
+
+ let messagefile = (||{
+ let messagefile = tempfile::tempfile().context("tempfile")?;
+ messagefile.write_all(message.as_bytes()).context("write")?;
+ messagefile.flush().context("flush")?;
+ messagefile.seek(SeekFrom::Start(0)).context("seek")?;
+ messagefile
+ })().context("write email to temporary file.")?;
+
+ let sendmail = &config().sendmail;
+ let command = Command::new(sendmail)
+ .args(&["-oee","-odb","-oi","-t","--"])
+ .stdin(messagefile)
+ .pre_exec(|| unsafe {
+ // https://github.com/rust-lang/rust/issues/79731
+ let r = libc::dup2(2,1);
+ assert_eq!(r, 1);
+ });
+ let st = command
+ .status()
+ .with_context(!! format!("run sendmail ({})", sendmail))?;
+ if !st.success() {
+ throw!(format!("sendmail ({}) failed: {} ({})", sendmail, st, st));
+ }
- AccessTokenReport { lines: todo!() }
+ AccessTokenReport { lines: vec![
+ "Token sent by email.".to_string()
+ ]}
}
}