1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
use std::{ffi::OsString, path::PathBuf, slice};

use widestring::{u16cstr, U16CStr};
use windows::Win32::Storage::CloudFilters::{CF_CALLBACK_INFO, CF_PROCESS_INFO};

pub type RawConnectionKey = i64;
pub type RawTransferKey = i64;

/// A struct containing various information for the current file operation.
///
/// If there is no activity on the placeholder (the methods in the
/// [Placeholder][crate::Placeholder] struct returned by
/// [Request::placeholder][crate::Request::placeholder]) within 60 seconds, the operating system
/// will automatically invalidate the request. To prevent this, read
/// [Request::reset_timeout][crate::Request::reset_timeout].
#[derive(Debug)]
pub struct Request(CF_CALLBACK_INFO);

impl Request {
    pub(crate) fn new(info: CF_CALLBACK_INFO) -> Self {
        Self(info)
    }

    /// The GUID path of the current volume.
    ///
    /// The returned value comes in the form `\?\Volume{GUID}`.
    pub fn volume_guid_path(&self) -> OsString {
        unsafe { U16CStr::from_ptr_str(self.0.VolumeGuidName.0) }.to_os_string()
    }

    /// The letter of the current volume.
    ///
    /// The returned value comes in the form `X:`, where `X` is the drive letter.
    pub fn volume_letter(&self) -> OsString {
        unsafe { U16CStr::from_ptr_str(self.0.VolumeDosName.0) }.to_os_string()
    }

    /// The serial number of the current volume.
    pub fn volume_serial_number(&self) -> u32 {
        self.0.VolumeSerialNumber
    }

    /// Information of the user process that triggered the callback.
    pub fn process(&self) -> Process {
        Process(unsafe { *self.0.ProcessInfo })
    }

    /// The NTFS file ID of the sync root folder under which the placeholder being operated on
    /// resides.
    pub fn sync_root_file_id(&self) -> i64 {
        self.0.SyncRootFileId
    }

    /// The NTFS file ID of the placeholder file/directory.
    pub fn file_id(&self) -> i64 {
        self.0.FileId
    }

    /// The logical size of the placeholder file.
    ///
    /// If the placeholder is a directory, this value will always equal 0.
    pub fn file_size(&self) -> u64 {
        self.0.FileSize as u64
    }

    // For now this should be cached on creation
    /// The absolute path of the placeholder file/directory starting from the root directory of the
    /// volume.
    ///
    /// [Read here for more information on this
    /// function.](https://docs.microsoft.com/en-us/windows/win32/api/cfapi/ns-cfapi-cf_callback_info#remarks)
    pub fn path(&self) -> PathBuf {
        let mut path =
            PathBuf::from(unsafe { U16CStr::from_ptr_str(self.0.VolumeDosName.0) }.to_os_string());
        path.push(unsafe { U16CStr::from_ptr_str(self.0.NormalizedPath.0) }.to_os_string());

        path
    }

    /// A numeric scale ranging from
    /// 0-[15](https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Storage/CloudFilters/constant.CF_MAX_PRIORITY_HINT.html)
    /// to describe the priority of the file operation.
    ///
    /// [Currently, this value does not
    /// change.](https://docs.microsoft.com/en-us/answers/questions/798674/priority-in-cf-callback-info.html)
    pub fn priority_hint(&self) -> u8 {
        self.0.PriorityHint
    }

    // https://docs.microsoft.com/en-us/answers/questions/749979/what-is-a-requestkey-cfapi.html
    // pub fn request_key(&self) -> i64 {
    //     self.0.RequestKey
    // }

    /// The byte slice assigned to the current placeholder file/directory.
    pub fn file_blob(&self) -> &[u8] {
        unsafe {
            slice::from_raw_parts(
                self.0.FileIdentity as *mut u8,
                self.0.FileIdentityLength as usize,
            )
        }
    }

    /// The byte slice assigned to the current sync root on registration.
    pub fn register_blob(&self) -> &[u8] {
        unsafe {
            slice::from_raw_parts(
                self.0.SyncRootIdentity as *mut u8,
                self.0.SyncRootIdentityLength as usize,
            )
        }
    }

    // // https://docs.microsoft.com/en-us/windows/win32/api/cfapi/ne-cfapi-cf_callback_type#remarks
    // // after 60 seconds of no report, windows will cancel the request with an error,
    // // this function is a "hack" to avoid the timeout
    // // https://docs.microsoft.com/en-us/windows/win32/api/cfapi/nf-cfapi-cfexecute#remarks
    // // CfExecute will reset any timers as stated
    // /// By default, the operating system will invalidate the callback after 60 seconds of no
    // /// activity (meaning, no placeholder methods are invoked). If you are prone to this issue,
    // /// consider calling this method or call placeholder methods more frequently.
    // pub fn reset_timeout() {}

    /// A raw connection key used to identify the connection.
    pub(crate) fn connection_key(&self) -> RawConnectionKey {
        self.0.ConnectionKey.0
    }

    /// A raw transfer key used to identify the current file operation.
    pub(crate) fn transfer_key(&self) -> RawTransferKey {
        self.0.TransferKey
    }
}

/// Information about the calling process.
#[derive(Debug)]
pub struct Process(CF_PROCESS_INFO);

impl Process {
    /// The application's package name.
    pub fn name(&self) -> OsString {
        unsafe { U16CStr::from_ptr_str(self.0.PackageName.0) }.to_os_string()
    }

    /// The ID of the user process.
    pub fn id(&self) -> u32 {
        self.0.ProcessId
    }

    /// The ID of the session where the user process resides.
    ///
    /// ## Note
    ///
    /// [session_id][crate::request::Process::session_id] is valid in versions 1803 and later.
    pub fn session_id(&self) -> u32 {
        self.0.SessionId
    }

    /// The application's ID.
    pub fn application_id(&self) -> OsString {
        unsafe { U16CStr::from_ptr_str(self.0.ApplicationId.0) }.to_os_string()
    }

    /// The exact command used to initialize the user process.
    ///
    /// ## Note
    ///
    /// [command_line][crate::request::Process::command_line] is valid in versions 1803 and later.
    pub fn command_line(&self) -> Option<OsString> {
        let cmd = unsafe { U16CStr::from_ptr_str(self.0.ImagePath.0) };
        (cmd != u16cstr!("UNKNOWN")).then(|| cmd.to_os_string())
    }

    // TODO: Could be optimized
    /// The absolute path to the main executable file of the process in the format of an NT path.
    ///
    /// This function returns [None][std::option::Option::None] when the operating system failed to
    /// retrieve the path.
    pub fn path(&self) -> Option<PathBuf> {
        let path = unsafe { U16CStr::from_ptr_str(self.0.ImagePath.0) };
        (path != u16cstr!("UNKNOWN")).then(|| PathBuf::from(path.to_os_string()))
    }
}