Skip to main content

outlook_mapi_sys/
installation.rs

1use std::path::{Path, PathBuf};
2use windows::{
3    Win32::Storage::FileSystem::GetBinaryTypeW,
4    core::{PCWSTR, w},
5};
6
7use crate::load_mapi::{
8    OFFICE_QUALIFIERS, OUTLOOK_QUALIFIED_COMPONENTS, get_office_executable_path,
9    get_office_mapi_path_no_install, get_outlook_mapi_path,
10};
11
12#[derive(Debug, Clone, Copy, PartialEq)]
13pub enum Architecture {
14    X64,
15    X86,
16}
17
18/// Represents the state of MAPI installation detection
19///
20/// The third boolean parameter in `Installed` indicates whether this is an
21/// official Outlook installation (true) or a fallback Office installation (false).
22/// Fallback installations are experimental and not officially supported.
23pub enum InstallationState {
24    /// MAPI installation found: (Architecture, DLL Path, Is Outlook Installation)
25    Installed {
26        /// Platform architecture that is installed.
27        architecture: Architecture,
28        /// Path to `olmapi32.dll`.
29        dll_path: PathBuf,
30        /// Indicates whether this is an official Outlook installation (true) or a fallback Office installation (false).
31        /// Fallback installations are experimental and not officially supported.
32        is_outlook_installed: bool,
33    },
34    NotInstalled,
35}
36
37fn get_binary_architecture(file_path: &Path) -> Result<Architecture, Box<dyn std::error::Error>> {
38    let path_str = file_path.to_string_lossy();
39    let path_wide: Vec<u16> = path_str.encode_utf16().chain(std::iter::once(0)).collect();
40    let mut binary_type: u32 = 0;
41
42    unsafe {
43        GetBinaryTypeW(PCWSTR::from_raw(path_wide.as_ptr()), &mut binary_type)?;
44
45        match binary_type {
46            0 => Ok(Architecture::X86), // SCS_32BIT_BINARY
47            6 => Ok(Architecture::X64), // SCS_64BIT_BINARY
48            _ => Err(format!("Unsupported binary type: {binary_type}").into()),
49        }
50    }
51}
52
53fn try_office_installation(category: PCWSTR, qualifier: PCWSTR) -> Option<InstallationState> {
54    // EXPERIMENTAL: Try to find MAPI through non-Outlook Office applications
55    // This is not officially supported and may not work reliably across all configurations.
56    // We are working on a better long-term approach for comprehensive MAPI detection.
57
58    // Try to get the executable path for architecture detection
59    let exe_path = unsafe { get_office_executable_path(category, qualifier) }.ok()?;
60
61    // Detect architecture from the executable
62    let actual_arch = get_binary_architecture(&exe_path).ok()?;
63
64    // Get the corresponding MAPI DLL path
65    let dll_path = unsafe { get_office_mapi_path_no_install(category, qualifier) }.ok()?;
66
67    Some(InstallationState::Installed {
68        architecture: actual_arch,
69        dll_path,
70        is_outlook_installed: false,
71    })
72}
73
74pub fn check_outlook_mapi_installation() -> InstallationState {
75    const OUTLOOK_QUALIFIERS: [(Architecture, PCWSTR); 2] = [
76        (Architecture::X64, w!("outlook.x64.exe")),
77        (Architecture::X86, w!("outlook.exe")),
78    ];
79
80    // First, try the standard Outlook qualified components (officially supported)
81    for category in OUTLOOK_QUALIFIED_COMPONENTS {
82        for (bitness, qualifier) in OUTLOOK_QUALIFIERS {
83            if let Ok(path) = unsafe { get_outlook_mapi_path(category, qualifier) } {
84                return InstallationState::Installed {
85                    architecture: bitness,
86                    dll_path: path,
87                    is_outlook_installed: true,
88                };
89            }
90        }
91    }
92
93    // EXPERIMENTAL FALLBACK: If Outlook is not found, try other Office applications
94    //
95    // WARNING: This fallback method is NOT officially supported by Microsoft.
96    // While technically functional, it relies on Office applications sharing MAPI
97    // infrastructure, which may not be guaranteed in future versions.
98    //
99    // We are actively working on a more robust long-term solution for comprehensive
100    // MAPI detection that will replace this experimental approach.
101    for category in OUTLOOK_QUALIFIED_COMPONENTS {
102        for qualifier in OFFICE_QUALIFIERS {
103            if let Some(installation) = try_office_installation(category, qualifier) {
104                return installation;
105            }
106        }
107    }
108
109    InstallationState::NotInstalled
110}