Rename to inbuxa-migrate

The crate, binary, library path and every name the tool writes or reads
become inbuxa-migrate: the CLI name and about text, the HTTP user agent,
the credential variables (INBUXA_MIGRATE_PASSWORD, _TOKEN,
_EWS_CLIENT_SECRET and _GRAPH_TOKEN, with no fallback to the old names),
thread names, synthesized UIDs and calendar addresses
(urn:x-inbuxa-migrate:attendee:), and the Maildir file suffix used by the
export script. This is a new tool, so archives written by Vandelay are not
read back.

The version moves to the date scheme the release tags use (2026.9.30), and
the package metadata points at the inbuxa-migrate repository. The cargo-dist
and MSI metadata goes with the release tooling it served.

Modified files carry an added copyright line; the original notices stay.
This commit is contained in:
2026-09-30 10:09:10 -07:00
parent 486a384712
commit 5931b06392
54 changed files with 719 additions and 644 deletions
+43 -38
View File
@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
* SPDX-FileCopyrightText: 2026 John Coffey <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -30,9 +31,9 @@ use crate::types::{ObjectType, parse_object_list};
#[derive(Parser)]
#[command(
name = "vandelay",
name = "inbuxa-migrate",
version,
about = "Vandelay: the JMAP importer-exporter"
about = "inbuxa-migrate: account migration and backup for inbuxa"
)]
pub struct Cli {
#[command(subcommand)]
@@ -138,7 +139,7 @@ struct JmapImportArgs {
#[arg(
long,
value_name = "PASS",
help = "Basic password (prefer $VANDELAY_PASSWORD or prompt)"
help = "Basic password (prefer $INBUXA_MIGRATE_PASSWORD or prompt)"
)]
auth_password: Option<String>,
@@ -146,7 +147,7 @@ struct JmapImportArgs {
long,
value_name = "TOKEN",
num_args = 0..=1,
help = "Bearer token (value optional: falls back to $VANDELAY_TOKEN or prompt)"
help = "Bearer token (value optional: falls back to $INBUXA_MIGRATE_TOKEN or prompt)"
)]
auth_bearer: Option<Option<String>>,
@@ -199,7 +200,7 @@ struct ImapImportArgs {
#[arg(
long,
value_name = "PASS",
help = "Basic password (prefer $VANDELAY_PASSWORD or prompt)"
help = "Basic password (prefer $INBUXA_MIGRATE_PASSWORD or prompt)"
)]
auth_password: Option<String>,
@@ -207,7 +208,7 @@ struct ImapImportArgs {
long,
value_name = "TOKEN",
num_args = 0..=1,
help = "Bearer token for XOAUTH2 (value optional: falls back to $VANDELAY_TOKEN or prompt)"
help = "Bearer token for XOAUTH2 (value optional: falls back to $INBUXA_MIGRATE_TOKEN or prompt)"
)]
auth_bearer: Option<Option<String>>,
@@ -309,7 +310,7 @@ struct ExportArgs {
#[arg(
long,
value_name = "PASS",
help = "Basic password (prefer $VANDELAY_PASSWORD or prompt)"
help = "Basic password (prefer $INBUXA_MIGRATE_PASSWORD or prompt)"
)]
auth_password: Option<String>,
@@ -317,7 +318,7 @@ struct ExportArgs {
long,
value_name = "TOKEN",
num_args = 0..=1,
help = "Bearer token (value optional: falls back to $VANDELAY_TOKEN or prompt)"
help = "Bearer token (value optional: falls back to $INBUXA_MIGRATE_TOKEN or prompt)"
)]
auth_bearer: Option<Option<String>>,
@@ -407,7 +408,7 @@ pub struct ManageSieveImportArgs {
#[arg(
long,
value_name = "PASS",
help = "Basic password (prefer $VANDELAY_PASSWORD or prompt)"
help = "Basic password (prefer $INBUXA_MIGRATE_PASSWORD or prompt)"
)]
auth_password: Option<String>,
@@ -415,7 +416,7 @@ pub struct ManageSieveImportArgs {
long,
value_name = "TOKEN",
num_args = 0..=1,
help = "Bearer token for OAUTHBEARER (value optional: $VANDELAY_TOKEN or prompt)"
help = "Bearer token for OAUTHBEARER (value optional: $INBUXA_MIGRATE_TOKEN or prompt)"
)]
auth_bearer: Option<Option<String>>,
@@ -457,7 +458,7 @@ pub struct DavImportArgs {
#[arg(
long,
value_name = "PASS",
help = "Basic password (prefer $VANDELAY_PASSWORD or prompt)"
help = "Basic password (prefer $INBUXA_MIGRATE_PASSWORD or prompt)"
)]
auth_password: Option<String>,
@@ -465,7 +466,7 @@ pub struct DavImportArgs {
long,
value_name = "TOKEN",
num_args = 0..=1,
help = "Bearer token (value optional: $VANDELAY_TOKEN or prompt)"
help = "Bearer token (value optional: $INBUXA_MIGRATE_TOKEN or prompt)"
)]
auth_bearer: Option<Option<String>>,
@@ -560,7 +561,7 @@ fn resolve_managesieve_auth(
"--auth-user is only valid with --auth-bearer".to_owned(),
));
}
let password = secret::resolve(auth_password, "VANDELAY_PASSWORD", "password")?;
let password = secret::resolve(auth_password, "INBUXA_MIGRATE_PASSWORD", "password")?;
return Ok(ManageSieveAuth::Basic {
user: user.to_owned(),
password,
@@ -577,7 +578,7 @@ fn resolve_managesieve_auth(
let user = auth_user
.ok_or_else(|| Error::Usage("--auth-user is required with --auth-bearer".to_owned()))?
.to_owned();
let token = secret::resolve(bearer.as_deref(), "VANDELAY_TOKEN", "bearer token")?;
let token = secret::resolve(bearer.as_deref(), "INBUXA_MIGRATE_TOKEN", "bearer token")?;
Ok(ManageSieveAuth::Bearer { user, token })
}
@@ -726,7 +727,7 @@ fn resolve_dav_auth(
auth_bearer: Option<&Option<String>>,
) -> Result<DavAuth, Error> {
if let Some(user) = auth_basic {
let password = secret::resolve(auth_password, "VANDELAY_PASSWORD", "password")?;
let password = secret::resolve(auth_password, "INBUXA_MIGRATE_PASSWORD", "password")?;
return Ok(DavAuth::Basic {
user: user.to_owned(),
password,
@@ -740,7 +741,7 @@ fn resolve_dav_auth(
let bearer = auth_bearer.ok_or_else(|| {
Error::Usage("exactly one of --auth-basic / --auth-bearer is required".to_owned())
})?;
let token = secret::resolve(bearer.as_deref(), "VANDELAY_TOKEN", "bearer token")?;
let token = secret::resolve(bearer.as_deref(), "INBUXA_MIGRATE_TOKEN", "bearer token")?;
Ok(DavAuth::Bearer { token })
}
@@ -828,7 +829,7 @@ fn resolve_imap_auth(
"--auth-user is only valid with --auth-bearer".to_owned(),
));
}
let password = secret::resolve(auth_password, "VANDELAY_PASSWORD", "password")?;
let password = secret::resolve(auth_password, "INBUXA_MIGRATE_PASSWORD", "password")?;
return Ok(ImapAuth::Basic {
user: user.to_owned(),
password,
@@ -845,7 +846,7 @@ fn resolve_imap_auth(
let user = auth_user
.ok_or_else(|| Error::Usage("--auth-user is required with --auth-bearer".to_owned()))?
.to_owned();
let token = secret::resolve(bearer.as_deref(), "VANDELAY_TOKEN", "bearer token")?;
let token = secret::resolve(bearer.as_deref(), "INBUXA_MIGRATE_TOKEN", "bearer token")?;
Ok(ImapAuth::Bearer { user, token })
}
@@ -921,7 +922,7 @@ pub struct ExchangeEwsImportArgs {
#[arg(
long,
value_name = "PASS",
help = "Basic password (prefer $VANDELAY_PASSWORD or prompt)"
help = "Basic password (prefer $INBUXA_MIGRATE_PASSWORD or prompt)"
)]
auth_password: Option<String>,
@@ -929,7 +930,7 @@ pub struct ExchangeEwsImportArgs {
long,
value_name = "TOKEN",
num_args = 0..=1,
help = "Bearer token (value optional: $VANDELAY_TOKEN or device-code flow)"
help = "Bearer token (value optional: $INBUXA_MIGRATE_TOKEN or device-code flow)"
)]
auth_bearer: Option<Option<String>>,
@@ -946,7 +947,7 @@ pub struct ExchangeEwsImportArgs {
#[arg(
long,
value_name = "SECRET",
help = "OAuth client secret (app-only flow; prefer $VANDELAY_EWS_CLIENT_SECRET)"
help = "OAuth client secret (app-only flow; prefer $INBUXA_MIGRATE_EWS_CLIENT_SECRET)"
)]
ews_client_secret: Option<String>,
@@ -1029,7 +1030,7 @@ fn resolve_exchange_ews_auth(args: &ExchangeEwsImportArgs) -> Result<EwsAuth, Er
if let Some(user) = args.auth_basic.as_deref() {
let password = secret::resolve(
args.auth_password.as_deref(),
"VANDELAY_PASSWORD",
"INBUXA_MIGRATE_PASSWORD",
"password",
)?;
return Ok(EwsAuth::Basic {
@@ -1066,7 +1067,7 @@ fn resolve_exchange_ews_auth(args: &ExchangeEwsImportArgs) -> Result<EwsAuth, Er
if let Some(secret) = args.ews_client_secret.clone() {
let resolved = secret::resolve(
Some(secret.as_str()),
"VANDELAY_EWS_CLIENT_SECRET",
"INBUXA_MIGRATE_EWS_CLIENT_SECRET",
"client secret",
)?;
return Ok(EwsAuth::OAuth(OAuthFlow::ClientCredentials {
@@ -1075,7 +1076,7 @@ fn resolve_exchange_ews_auth(args: &ExchangeEwsImportArgs) -> Result<EwsAuth, Er
client_secret: resolved,
}));
}
if let Ok(env_secret) = std::env::var("VANDELAY_EWS_CLIENT_SECRET")
if let Ok(env_secret) = std::env::var("INBUXA_MIGRATE_EWS_CLIENT_SECRET")
&& !env_secret.is_empty()
{
return Ok(EwsAuth::OAuth(OAuthFlow::ClientCredentials {
@@ -1084,7 +1085,7 @@ fn resolve_exchange_ews_auth(args: &ExchangeEwsImportArgs) -> Result<EwsAuth, Er
client_secret: env_secret,
}));
}
let token = secret::resolve(None, "VANDELAY_TOKEN", "bearer token")?;
let token = secret::resolve(None, "INBUXA_MIGRATE_TOKEN", "bearer token")?;
Ok(EwsAuth::Bearer { token })
}
@@ -1125,7 +1126,7 @@ pub struct ExchangeGraphImportArgs {
long,
value_name = "TOKEN",
num_args = 0..=1,
help = "Pre-acquired bearer token (skip device-code flow). Resolves: flag value -> $VANDELAY_GRAPH_TOKEN -> prompt."
help = "Pre-acquired bearer token (skip device-code flow). Resolves: flag value -> $INBUXA_MIGRATE_GRAPH_TOKEN -> prompt."
)]
access_token: Option<Option<String>>,
@@ -1168,7 +1169,7 @@ pub struct ExchangeGraphImportArgs {
help = "Years either side of today to scan for edited recurrence occurrences (max 5)",
long_help = "Years either side of today to scan for edited recurrence occurrences.\n \
Graph reports a series exception only inside an expanded calendar view, and caps\n \
any single view at five years, so vandelay scans [today - N, today] and\n \
any single view at five years, so inbuxa-migrate scans [today - N, today] and\n \
[today, today + N]. An occurrence edited outside that span imports with the\n \
series default instead of its edit."
)]
@@ -1177,7 +1178,7 @@ pub struct ExchangeGraphImportArgs {
#[arg(
long,
help = "Skip contact photos (saves one request per contact on large address books)",
long_help = "Skip contact photos.\n Graph will not say which contacts have a photo: $expand=photo comes back empty\n even when one exists, so the only way to find out is to ask for the bytes.\n vandelay therefore spends one extra request per contact. Pass this to skip\n photos entirely on a large address book."
long_help = "Skip contact photos.\n Graph will not say which contacts have a photo: $expand=photo comes back empty\n even when one exists, so the only way to find out is to ask for the bytes.\n inbuxa-migrate therefore spends one extra request per contact. Pass this to skip\n photos entirely on a large address book."
)]
skip_contact_photos: bool,
@@ -1265,7 +1266,11 @@ fn resolve_graph_auth(
tenant: &str,
) -> Result<GraphAuth, Error> {
if let Some(slot) = access_token {
let token = secret::resolve(slot.as_deref(), "VANDELAY_GRAPH_TOKEN", "bearer token")?;
let token = secret::resolve(
slot.as_deref(),
"INBUXA_MIGRATE_GRAPH_TOKEN",
"bearer token",
)?;
return Ok(GraphAuth::PreAcquired { token });
}
let client_id = client_id
@@ -1305,7 +1310,7 @@ fn resolve_auth(
auth_bearer: Option<&Option<String>>,
) -> Result<Auth, Error> {
if let Some(user) = auth_basic {
let password = secret::resolve(auth_password, "VANDELAY_PASSWORD", "password")?;
let password = secret::resolve(auth_password, "INBUXA_MIGRATE_PASSWORD", "password")?;
return Ok(Auth::Basic {
user: user.to_owned(),
password,
@@ -1319,7 +1324,7 @@ fn resolve_auth(
let bearer = auth_bearer.ok_or_else(|| {
Error::Usage("exactly one of --auth-basic / --auth-bearer is required".to_owned())
})?;
let token = secret::resolve(bearer.as_deref(), "VANDELAY_TOKEN", "bearer token")?;
let token = secret::resolve(bearer.as_deref(), "INBUXA_MIGRATE_TOKEN", "bearer token")?;
Ok(Auth::Bearer { token })
}
@@ -1350,8 +1355,8 @@ mod tests {
fn auth_password_without_basic_is_usage_error() {
let _g = lock();
unsafe {
env::remove_var("VANDELAY_TOKEN");
env::remove_var("VANDELAY_PASSWORD");
env::remove_var("INBUXA_MIGRATE_TOKEN");
env::remove_var("INBUXA_MIGRATE_PASSWORD");
}
let bearer: Option<String> = Some("t".to_owned());
let r = resolve_auth(None, Some("hunter2"), Some(&bearer));
@@ -1367,8 +1372,8 @@ mod tests {
fn auth_basic_with_password_resolves() {
let _g = lock();
unsafe {
env::remove_var("VANDELAY_TOKEN");
env::remove_var("VANDELAY_PASSWORD");
env::remove_var("INBUXA_MIGRATE_TOKEN");
env::remove_var("INBUXA_MIGRATE_PASSWORD");
}
let auth = resolve_auth(Some("alice"), Some("pw"), None).unwrap();
match auth {
@@ -1384,8 +1389,8 @@ mod tests {
fn auth_bearer_inline_token_resolves() {
let _g = lock();
unsafe {
env::remove_var("VANDELAY_TOKEN");
env::remove_var("VANDELAY_PASSWORD");
env::remove_var("INBUXA_MIGRATE_TOKEN");
env::remove_var("INBUXA_MIGRATE_PASSWORD");
}
let bearer: Option<String> = Some("t".to_owned());
let auth = resolve_auth(None, None, Some(&bearer)).unwrap();
@@ -1394,7 +1399,7 @@ mod tests {
fn graph_config(objects: &[&str]) -> Result<GraphImportConfig, Error> {
let mut argv = vec![
"vandelay",
"inbuxa-migrate",
"import",
"exchange-graph",
"--access-token",
+2 -1
View File
@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
* SPDX-FileCopyrightText: 2026 John Coffey <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -87,7 +88,7 @@ impl DavClient {
log_level: AtomicU8::new(LEVEL_DEFAULT),
retries_total: AtomicU64::new(0),
retry_after_sleeps: AtomicU64::new(0),
user_agent: format!("vandelay/{}", env!("CARGO_PKG_VERSION")),
user_agent: format!("inbuxa-migrate/{}", env!("CARGO_PKG_VERSION")),
}),
}
}
+5 -4
View File
@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
* SPDX-FileCopyrightText: 2026 John Coffey <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -36,7 +37,7 @@ const CALENDAR_ADDRESS_DEPENDENTS: &[&str] = &[
pub fn synthetic_attendee_address(identifier: &str) -> String {
format!(
"urn:x-vandelay:attendee:{}",
"urn:x-inbuxa-migrate:attendee:{}",
blake3::hash(identifier.as_bytes()).to_hex()
)
}
@@ -146,7 +147,7 @@ mod tests {
let event = json!({
"@type": "Event",
"uid": "uid-1",
"prodId": "vandelay",
"prodId": "inbuxa-migrate",
"privacy": "public",
"organizerCalendarAddress": "mailto:[email protected]",
"recurrenceRule": {"frequency": "daily"},
@@ -173,7 +174,7 @@ mod tests {
let addr = synthetic_attendee_address("Jane Doe");
assert_eq!(addr, synthetic_attendee_address("Jane Doe"));
assert_ne!(addr, synthetic_attendee_address("John Doe"));
assert!(addr.starts_with("urn:x-vandelay:attendee:"));
assert!(addr.starts_with("urn:x-inbuxa-migrate:attendee:"));
assert!(
!addr.starts_with("mailto:"),
"export must never invite a fabricated address"
@@ -186,7 +187,7 @@ mod tests {
"1": {
"@type": "Participant",
"name": "Room 4",
"calendarAddress": "urn:x-vandelay:attendee:abc",
"calendarAddress": "urn:x-inbuxa-migrate:attendee:abc",
"email": "[email protected]",
"roles": {"required": true},
"kind": "resource",
+6 -5
View File
@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
* SPDX-FileCopyrightText: 2026 John Coffey <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -53,7 +54,7 @@ fn build_event_map(raw: &CalendarItemRaw, iana: Option<&str>) -> Map<String, Val
event.insert(
"uid".to_owned(),
Value::String(format!(
"vandelay-ews-event-{}",
"inbuxa-migrate-ews-event-{}",
blake3::hash(raw.id.id.as_bytes()).to_hex()
)),
);
@@ -723,7 +724,7 @@ mod tests {
.unwrap();
let addr = p["calendarAddress"].as_str().unwrap();
assert!(
addr.starts_with("urn:x-vandelay:attendee:"),
addr.starts_with("urn:x-inbuxa-migrate:attendee:"),
"a name-only attendee gets a stable synthetic calendarAddress, got {addr}"
);
assert!(
@@ -771,7 +772,7 @@ mod tests {
let v = to_jscalendar(&raw).data;
let org_addr = v["organizerCalendarAddress"].as_str().unwrap();
assert!(
org_addr.starts_with("urn:x-vandelay:attendee:") && !org_addr.contains("mailto:"),
org_addr.starts_with("urn:x-inbuxa-migrate:attendee:") && !org_addr.contains("mailto:"),
"an EX organizer must not become mailto:/o=.../cn=...; got {org_addr}"
);
let att = v["participants"]
@@ -921,7 +922,7 @@ mod tests {
let v = to_jscalendar(&raw).data;
let addr = v["organizerCalendarAddress"].as_str().unwrap();
assert!(
addr.starts_with("urn:x-vandelay:attendee:"),
addr.starts_with("urn:x-inbuxa-migrate:attendee:"),
"an unresolvable directory reference stays synthetic, got {addr}"
);
let participants = v["participants"].as_object().unwrap();
@@ -947,7 +948,7 @@ mod tests {
v["organizerCalendarAddress"]
.as_str()
.unwrap()
.starts_with("urn:x-vandelay:attendee:")
.starts_with("urn:x-inbuxa-migrate:attendee:")
);
}
+2 -1
View File
@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
* SPDX-FileCopyrightText: 2026 John Coffey <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -81,7 +82,7 @@ impl EwsClient {
retries_total: AtomicU64::new(0),
retry_after_sleeps: AtomicU64::new(0),
soap_calls: AtomicU64::new(0),
user_agent: format!("vandelay/{}", env!("CARGO_PKG_VERSION")),
user_agent: format!("inbuxa-migrate/{}", env!("CARGO_PKG_VERSION")),
}),
}
}
+3 -2
View File
@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
* SPDX-FileCopyrightText: 2026 John Coffey <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -10,7 +11,7 @@ use crate::exchange_ews::parse::{ContactItemRaw, RawContactAddress};
pub fn synthetic_uid(item_id: &str) -> String {
let hash = blake3::hash(item_id.as_bytes()).to_hex();
format!("vandelay-ews-syn-{hash}")
format!("inbuxa-migrate-ews-syn-{hash}")
}
pub fn to_jscontact(raw: &ContactItemRaw) -> Value {
@@ -389,7 +390,7 @@ mod tests {
let u2 = synthetic_uid("ITEM-1");
assert_eq!(u1, u2);
assert_ne!(u1, synthetic_uid("ITEM-2"));
assert!(u1.starts_with("vandelay-ews-syn-"));
assert!(u1.starts_with("inbuxa-migrate-ews-syn-"));
}
#[test]
+2 -1
View File
@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
* SPDX-FileCopyrightText: 2026 John Coffey <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -724,7 +725,7 @@ fn wrap_inner_with_namespaces(inner: &str) -> String {
return String::new();
}
format!(
"<vandelay-inner xmlns=\"{NS_TYPES}\" xmlns:t=\"{NS_TYPES}\" xmlns:m=\"{NS_MESSAGES}\">{inner}</vandelay-inner>",
"<inbuxa-migrate-inner xmlns=\"{NS_TYPES}\" xmlns:t=\"{NS_TYPES}\" xmlns:m=\"{NS_MESSAGES}\">{inner}</inbuxa-migrate-inner>",
)
}
+4 -3
View File
@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
* SPDX-FileCopyrightText: 2026 John Coffey <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -701,7 +702,7 @@ mod tests {
let p = &conv.data["participants"]["att-1"];
let addr = p["calendarAddress"].as_str().unwrap();
assert!(
addr.starts_with("urn:x-vandelay:attendee:"),
addr.starts_with("urn:x-inbuxa-migrate:attendee:"),
"a hybrid X500 reference must not become a malformed mailto, got {addr}"
);
assert!(
@@ -755,7 +756,7 @@ mod tests {
let p = &conv.data["participants"]["att-1"];
let addr = p["calendarAddress"].as_str().unwrap();
assert!(
addr.starts_with("urn:x-vandelay:attendee:"),
addr.starts_with("urn:x-inbuxa-migrate:attendee:"),
"a name-only attendee needs a synthetic calendarAddress, got {addr}"
);
assert!(
@@ -799,7 +800,7 @@ mod tests {
v["organizer"] = json!({"emailAddress": {"name": "Alice Example"}});
let conv = convert_event(&v, None).unwrap();
let organizer_address = conv.data["organizerCalendarAddress"].as_str().unwrap();
assert!(organizer_address.starts_with("urn:x-vandelay:attendee:"));
assert!(organizer_address.starts_with("urn:x-inbuxa-migrate:attendee:"));
assert_eq!(
conv.data["participants"]["organizer"]["calendarAddress"],
organizer_address
+2 -1
View File
@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
* SPDX-FileCopyrightText: 2026 John Coffey <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -112,7 +113,7 @@ impl GraphClient {
retries_total: AtomicU64::new(0),
retry_after_sleeps: AtomicU64::new(0),
requests_total: AtomicU64::new(0),
user_agent: format!("vandelay/{}", env!("CARGO_PKG_VERSION")),
user_agent: format!("inbuxa-migrate/{}", env!("CARGO_PKG_VERSION")),
}),
}
}
+3 -2
View File
@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
* SPDX-FileCopyrightText: 2026 John Coffey <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -354,7 +355,7 @@ fn has_uri_scheme(value: &str) -> bool {
pub fn synthetic_uid(graph_id: &str) -> String {
let hash = blake3::hash(graph_id.as_bytes());
format!("vandelay-graph-{}", hash.to_hex())
format!("inbuxa-migrate-graph-{}", hash.to_hex())
}
fn normalise_utc_datetime(raw: &str) -> String {
@@ -465,7 +466,7 @@ mod tests {
fn minimal_contact_yields_card_with_synthetic_uid() {
let c = json!({"id": "GRAPH-1", "displayName": "Alice"});
let conv = convert_contact(&c).unwrap();
assert!(conv.uid.starts_with("vandelay-graph-"));
assert!(conv.uid.starts_with("inbuxa-migrate-graph-"));
assert_eq!(conv.data["@type"], "Card");
assert_eq!(conv.data["kind"], "individual");
assert_eq!(conv.data["name"]["full"], "Alice");
+2 -1
View File
@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
* SPDX-FileCopyrightText: 2026 John Coffey <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -18,7 +19,7 @@ impl MailboxKind {
"primary" => Ok(MailboxKind::Primary),
"archive" => Ok(MailboxKind::Archive),
"public-folders" => Err(Error::Usage(
"Microsoft Graph does not expose public folders. Run `vandelay import exchange-ews --mailbox-kind public-folders` instead.".to_owned(),
"Microsoft Graph does not expose public folders. Run `inbuxa-migrate import exchange-ews --mailbox-kind public-folders` instead.".to_owned(),
)),
other => Err(Error::Usage(format!(
"--mailbox-kind must be primary | archive, got {other:?}"
+2 -1
View File
@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
* SPDX-FileCopyrightText: 2026 John Coffey <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -139,7 +140,7 @@ impl std::fmt::Display for OriginMismatch {
}
write!(
f,
" on {} (connected to {}); vandelay must use the advertised URL. \
" on {} (connected to {}); inbuxa-migrate must use the advertised URL. \
If this is wrong, fix the server's advertised HTTP URL setting.",
self.session_origin, self.connect_origin
)
+5 -4
View File
@@ -1,15 +1,16 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
* SPDX-FileCopyrightText: 2026 John Coffey <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
use clap::Parser;
use vandelay::cli::{Action, Cli};
use vandelay::error::Error;
use vandelay::inspect;
use vandelay::sync::{self, RunOutcome, Summary};
use inbuxa_migrate::cli::{Action, Cli};
use inbuxa_migrate::error::Error;
use inbuxa_migrate::inspect;
use inbuxa_migrate::sync::{self, RunOutcome, Summary};
fn main() {
let code = run();
+3 -2
View File
@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
* SPDX-FileCopyrightText: 2026 John Coffey <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -52,7 +53,7 @@ pub fn vcard_to_jscontact(text: &str, item_href: &str) -> Result<JsContact, Calc
pub fn synthesise_uid(item_href: &str) -> String {
let hash = blake3::hash(item_href.as_bytes());
format!("vandelay-syn-{}", hash.to_hex())
format!("inbuxa-migrate-syn-{}", hash.to_hex())
}
#[derive(Debug)]
@@ -208,7 +209,7 @@ END:VCALENDAR\r\n";
let href = "/dav/card/u/d/no-uid.vcf";
let jc = vcard_to_jscontact(no_uid, href).expect("synthetic uid");
assert_eq!(jc.uid, synthesise_uid(href));
assert!(jc.uid.starts_with("vandelay-syn-"));
assert!(jc.uid.starts_with("inbuxa-migrate-syn-"));
}
#[test]
+2 -1
View File
@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
* SPDX-FileCopyrightText: 2026 John Coffey <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -385,7 +386,7 @@ fn spawn_token_refresher(
let client = client.clone();
let initial_refresh = initial.as_ref().and_then(|t| t.refresh_token.clone());
std::thread::Builder::new()
.name("vandelay-ews-token-refresh".to_owned())
.name("inbuxa-migrate-ews-token-refresh".to_owned())
.spawn(move || {
let mut next_exp = exp_secs;
let mut refresh_token = initial_refresh;
@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
* SPDX-FileCopyrightText: 2026 John Coffey <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -395,7 +396,7 @@ fn spawn_token_refresher(
let shutdown = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
let shutdown_signal = shutdown.clone();
let handle = std::thread::Builder::new()
.name("vandelay-graph-token-refresh".to_owned())
.name("inbuxa-migrate-graph-token-refresh".to_owned())
.spawn(move || {
while !shutdown_signal.load(std::sync::atomic::Ordering::Relaxed) {
let now = unix_now();
+3 -2
View File
@@ -1,5 +1,6 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
* SPDX-FileCopyrightText: 2026 John Coffey <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
@@ -199,7 +200,7 @@ pub fn run(common: CommonConfig, config: ManageSieveImportConfig) -> Result<Summ
}
Disposition::Referral => {
return Err(Error::Connection(format!(
"ManageSieve referral on GETSCRIPT {name:?}: {e}; vandelay does not follow referrals"
"ManageSieve referral on GETSCRIPT {name:?}: {e}; inbuxa-migrate does not follow referrals"
)));
}
Disposition::TransportDrop => {
@@ -536,7 +537,7 @@ fn listscripts_with_retry(
}
Disposition::Referral => {
return Err(Error::Connection(format!(
"LISTSCRIPTS referral: {e}; vandelay does not follow referrals"
"LISTSCRIPTS referral: {e}; inbuxa-migrate does not follow referrals"
)));
}
_ => return Err(Error::Connection(format!("LISTSCRIPTS failed: {e}"))),