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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
//! Definitions for the  
//! [ShellLinkHeader](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-shllink/c3376b21-0931-45e4-b2fc-a48ac0e60d15)
//! type.
//!

use bitflags::bitflags;
use byteorder::{ReadBytesExt, LE};
use std::io::Cursor;

#[repr(C)]
#[derive(Debug, Clone, Copy)]
/// The ShellLinkHeader structure contains identification information, timestamps, and flags that specify
/// the presence of optional structures, including LinkTargetIDList (section 2.2), LinkInfo (section 2.3),
/// and StringData (section 2.4).
pub struct ShellLinkHeader {
    /// The size, in bytes, of this structure. This value MUST be 0x0000004C.
    pub header_size: u32,

    /// A class identifier (CLSID). This value MUST be 00021401-0000-0000-C000-000000000046.
    pub link_clsid: u128,

    /// A LinkFlags structure (section 2.1.1) that specifies information about the shell
    /// link and the presence of optional portions of the structure.
    pub link_flags: LinkFlags,

    /// FileAttributes (4 bytes): A FileAttributesFlags structure (section 2.1.2) that specifies information
    /// about the link target.
    pub file_attributes: FileAttributeFlags,

    /// A FILETIME structure ([MS-DTYP] section 2.3.3) that specifies the creation
    /// time of the link target in UTC (Coordinated Universal Time). If the value is zero, there is no
    /// creation time set on the link target.
    pub creation_time: u64,

    /// A FILETIME structure ([MS-DTYP] section 2.3.3) that specifies the access
    /// time of the link target in UTC (Coordinated Universal Time). If the value is zero, there is no access
    /// time set on the link target.
    pub access_time: u64,

    /// A FILETIME structure ([MS-DTYP] section 2.3.3) that specifies the write time
    /// of the link target in UTC (Coordinated Universal Time). If the value is zero, there is no write time
    /// set on the link target.
    pub write_time: u64,

    /// A 32-bit unsigned integer that specifies the size, in bytes, of the link target. If the
    /// link target file is larger than 0xFFFFFFFF, this value specifies the least significant 32 bits of the link
    /// target file size.
    pub file_size: u32,

    /// IconIndex (4 bytes): A 32-bit signed integer that specifies the index of an icon within a given icon
    /// location.
    pub icon_index: u32,

    /// ShowCommand (4 bytes): A 32-bit unsigned integer that specifies the expected
    pub show_command: ShowCommand,

    /// HotKey (2 bytes): A HotKeyFlags structure (section 2.1.3) that specifies the keystrokes used to
    /// launch the application referenced by the shortcut key. This value is assigned to the application
    /// after it is launched, so that pressing the key activates that application.
    pub hot_key: HotKeyFlags,

    /// Reserved1 (2 bytes): A value that MUST be zero.
    pub reserved1: u16,

    /// Reserved2 (4 bytes): A value that MUST be zero.
    pub reserved2: u32,

    /// Reserved3 (4 bytes): A value that MUST be zero.
    pub reserved3: u32,

    /// Human readable created on date
    #[cfg(feature = "chrono")]
    pub created_on: Option<chrono::DateTime<chrono::Utc>>,

    /// Human readable modified on date
    #[cfg(feature = "chrono")]
    pub modified_on: Option<chrono::DateTime<chrono::Utc>>,

    /// Human readable accessed on date
    #[cfg(feature = "chrono")]
    pub accessed_on: Option<chrono::DateTime<chrono::Utc>>,
}

impl std::convert::TryFrom<&mut Cursor<Vec<u8>>> for ShellLinkHeader {
    type Error = crate::error::HeaderError;
    fn try_from(cursor: &mut Cursor<Vec<u8>>) -> Result<Self, Self::Error> {
        let mut header = Self {
            header_size: cursor.read_u32::<LE>().map_err(Self::Error::Read)?,
            link_clsid: cursor.read_u128::<LE>().map_err(Self::Error::Read)?,
            link_flags: LinkFlags::from_bits_truncate(
                cursor.read_u32::<LE>().map_err(Self::Error::Read)?,
            ),
            file_attributes: FileAttributeFlags::from_bits_truncate(
                cursor.read_u32::<LE>().map_err(Self::Error::Read)?,
            ),
            creation_time: cursor.read_u64::<LE>().map_err(Self::Error::Read)?,
            access_time: cursor.read_u64::<LE>().map_err(Self::Error::Read)?,
            write_time: cursor.read_u64::<LE>().map_err(Self::Error::Read)?,
            file_size: cursor.read_u32::<LE>().map_err(Self::Error::Read)?,
            icon_index: cursor.read_u32::<LE>().map_err(Self::Error::Read)?,
            show_command: ShowCommand::from_bits_truncate(
                cursor.read_u32::<LE>().map_err(Self::Error::Read)?,
            ),
            hot_key: HotKeyFlags::from(cursor.read_u16::<LE>().map_err(Self::Error::Read)?),
            reserved1: cursor.read_u16::<LE>().map_err(Self::Error::Read)?,
            reserved2: cursor.read_u32::<LE>().map_err(Self::Error::Read)?,
            reserved3: cursor.read_u32::<LE>().map_err(Self::Error::Read)?,
            #[cfg(feature = "chrono")]
            created_on: None,
            #[cfg(feature = "chrono")]
            modified_on: None,
            #[cfg(feature = "chrono")]
            accessed_on: None,
        };

        #[cfg(feature = "chrono")]
        {
            use chrono::{TimeZone, Utc};

            let start = Utc.ymd(1601, 1, 1).and_hms(0, 0, 0);

            header.created_on =
                Some(start + chrono::Duration::milliseconds(header.creation_time as i64 / 10000));

            header.modified_on =
                Some(start + chrono::Duration::milliseconds(header.write_time as i64 / 10000));

            header.accessed_on =
                Some(start + chrono::Duration::milliseconds(header.access_time as i64 / 10000));
        }

        Ok(header)
    }
}

bitflags! {
    /// The LinkFlags structure defines bits that specify which shell link structures are present in the file
    /// format after the ShellLinkHeader structure (section 2.1).
    pub struct LinkFlags: u32 {
        /// The shell link is saved with an item ID list (IDList). If this bit is set, a
        /// LinkTargetIDList structure (section 2.2) MUST follow the ShellLinkHeader.
        /// If this bit is not set, this structure MUST NOT be present.
        const HAS_LINK_TARGET_ID_LIST           = 0b0000_0000_0000_0000_0000_0000_0000_0001;

        /// The shell link is saved with link information. If this bit is set, a LinkInfo
        /// structure (section 2.3) MUST be present. If this bit is not set, this structure
        /// MUST NOT be present.
        const HAS_LINK_INFO                     = 0b0000_0000_0000_0000_0000_0000_0000_0010;

        ///The shell link is saved with a name string. If this bit is set, a
        ///NAME_STRING StringData structure (section 2.4) MUST be present. If
        ///this bit is not set, this structure MUST NOT be present.
        const HAS_NAME                          = 0b0000_0000_0000_0000_0000_0000_0000_0100;

        /// The shell link is saved with a relative path string. If this bit is set, a
        /// RELATIVE_PATH StringData structure (section 2.4) MUST be present. If
        /// this bit is not set, this structure MUST NOT be present.
        const HAS_RELATIVE_PATH                 = 0b0000_0000_0000_0000_0000_0000_0000_1000;

        /// The shell link is saved with a working directory string. If this bit is set, a
        /// WORKING_DIR StringData structure (section 2.4) MUST be present. If
        /// this bit is not set, this structure MUST NOT be present.
        const HAS_WORKING_DIR                   = 0b0000_0000_0000_0000_0000_0000_0001_0000;

        /// The shell link is saved with command line arguments. If this bit is set, a
        /// COMMAND_LINE_ARGUMENTS StringData structure (section 2.4) MUST
        /// be present. If this bit is not set, this structure MUST NOT be present.
        const HAS_ARGUMENTS                     = 0b0000_0000_0000_0000_0000_0000_0010_0000;

        /// The shell link is saved with an icon location string. If this bit is set, an
        /// ICON_LOCATION StringData structure (section 2.4) MUST be present. If
        /// this bit is not set, this structure MUST NOT be present.
        const HAS_ICON_LOCATION                 = 0b0000_0000_0000_0000_0000_0000_0100_0000;

        /// The shell link contains Unicode encoded strings. This bit SHOULD be set. If
        /// this bit is set, the StringData section contains Unicode-encoded strings;
        /// otherwise, it contains strings that are encoded using the system default
        /// code page.
        const IS_UNICODE                        = 0b0000_0000_0000_0000_0000_0000_1000_0000;

        /// The LinkInfo structure (section 2.3) is ignored.
        const FORCE_NO_LINK_INFO                = 0b0000_0000_0000_0000_0000_0001_0000_0000;

        /// The shell link is saved with an
        /// EnvironmentVariableDataBlock (section 2.5.4).
        const HAS_EXP_STRING                    = 0b0000_0000_0000_0000_0000_0010_0000_0000;

        /// The target is run in a separate virtual machine when launching a link
        /// target that is a 16-bit application.
        const RUN_IN_SEPARATE_PROCESS           = 0b0000_0000_0000_0000_0000_0100_0000_0000;

        /// A bit that is undefined and MUST be ignored.
        const UNUSED_1                          = 0b0000_0000_0000_0000_0000_1000_0000_0000;

        /// The shell link is saved with a DarwinDataBlock (section 2.5.3).
        const HAS_DARWIN_ID                     = 0b0000_0000_0000_0000_0001_0000_0000_0000;

        /// The application is run as a different user when the target of the shell link is
        /// activated.
        const RUN_AS_USER                       = 0b0000_0000_0000_0000_0010_0000_0000_0000;

        /// The shell link is saved with an IconEnvironmentDataBlock (section 2.5.5).
        const HAS_EXP_ICON                      = 0b0000_0000_0000_0000_0100_0000_0000_0000;

        /// The file system location is represented in the shell namespace when the
        /// path to an item is parsed into an IDList.
        const NO_PID_I_ALIAS                    = 0b0000_0000_0000_0000_1000_0000_0000_0000;

        /// A bit that is undefined and MUST be ignored.
        const UNUSED_2                          = 0b0000_0000_0000_0001_0000_0000_0000_0000;

        /// The shell link is saved with a ShimDataBlock (section 2.5.8).
        const RUN_WITH_SHIM_LAYER               = 0b0000_0000_0000_0010_0000_0000_0000_0000;

        /// The TrackerDataBlock (section 2.5.10) is ignored.
        const FORCE_NO_LINK_TRACK               = 0b0000_0000_0000_0100_0000_0000_0000_0000;

        /// The shell link attempts to collect target properties and store them in the
        /// PropertyStoreDataBlock (section 2.5.7) when the link target is set.
        const ENABLE_TARGET_METADATA            = 0b0000_0000_0000_1000_0000_0000_0000_0000;

        /// The EnvironmentVariableDataBlock is ignored.
        const DISABLE_LINK_PATH_TRACKING        = 0b0000_0000_0001_0000_0000_0000_0000_0000;

        /// The SpecialFolderDataBlock (section 2.5.9) and the
        /// KnownFolderDataBlock (section 2.5.6) are ignored when loading the shell
        /// link. If this bit is set, these extra data blocks SHOULD NOT be saved when
        /// saving the shell link.
        const DISABLE_KNOWN_FOLDER_TRACKING     = 0b0000_0000_0010_0000_0000_0000_0000_0000;

        /// If the link has a KnownFolderDataBlock (section 2.5.6), the unaliased form
        /// of the known folder IDList SHOULD be used when translating the target
        /// IDList at the time that the link is loaded.
        const DISABLE_KNOWN_FOLDER_ALIAS        = 0b0000_0000_0100_0000_0000_0000_0000_0000;

        /// Creating a link that references another link is enabled. Otherwise,
        /// specifying a link as the target IDList SHOULD NOT be allowed.
        const ALLOW_LINK_TO_LINK                = 0b0000_0000_1000_0000_0000_0000_0000_0000;

        /// When saving a link for which the target IDList is under a known folder,
        /// either the unaliased form of that known folder or the target IDList SHOULD
        /// be used.
        const UNALIAS_ON_SAVE                   = 0b0000_0001_0000_0000_0000_0000_0000_0000;

        /// The target IDList SHOULD NOT be stored; instead, the path specified in the
        /// EnvironmentVariableDataBlock (section 2.5.4) SHOULD be used to refer to
        /// the target.
        const PREFER_ENVIRONMENT_PATH           = 0b0000_0010_0000_0000_0000_0000_0000_0000;

        /// When the target is a UNC name that refers to a location on a local
        /// machine, the local path IDList in the
        /// PropertyStoreDataBlock (section 2.5.7) SHOULD be stored, so it can be
        /// used when the link is loaded on the local machine.
        const KEEP_LOCAL_ID_LIST_FOR_UNC_TARGET = 0b0000_0100_0000_0000_0000_0000_0000_0000;
    }
}

bitflags! {
    /// The FileAttributesFlags structure defines bits that specify the file attributes of the link target, if the
    /// target is a file system item. File attributes can be used if the link target is not available, or if accessing
    /// the target would be inefficient. It is possible for the target items attributes to be out of sync with this
    /// value.
    pub struct FileAttributeFlags: u32 {
        /// The file or directory is read-only. For a file, if this bit is set, applications can read the file but cannot write to it or delete it. For a directory, if this bit is set, applications cannot delete the directory.
        const FILE_ATTRIBUTE_READONLY               = 0b1000_0000_0000_0000_0000_0000_0000_0000;

        /// The file or directory is hidden. If this bit is set, the file or folder is not included in an ordinary directory listing.
        const FILE_ATTRIBUTE_HIDDEN                 = 0b0100_0000_0000_0000_0000_0000_0000_0000;

        /// The file or directory is part of the operating system or is used exclusively by the operating system.
        const FILE_ATTRIBUTE_SYSTEM                 = 0b0010_0000_0000_0000_0000_0000_0000_0000;

        /// A bit that MUST be zero.
        const RESERVED_1                            = 0b0001_0000_0000_0000_0000_0000_0000_0000;

        /// The link target is a directory instead of a file.
        const FILE_ATTRIBUTE_DIRECTORY              = 0b0000_1000_0000_0000_0000_0000_0000_0000;

        /// The file or directory is an archive file. Applications use this flag to mark files for backup or removal.
        const FILE_ATTRIBUTE_ARCHIVE                = 0b0000_0100_0000_0000_0000_0000_0000_0000;

        /// A bit that MUST be zero.
        const RESERVED_2                            = 0b0000_0010_0000_0000_0000_0000_0000_0000;

        /// The file or directory has no other flags set. If this bit is 1, all other bits in this structure MUST be clear.
        const FILE_ATTRIBUTE_NORMAL                 = 0b0000_0001_0000_0000_0000_0000_0000_0000;

        /// The file is being used for temporary storage.
        const FILE_ATTRIBUTE_TEMPORARY              = 0b0000_0000_1000_0000_0000_0000_0000_0000;

        /// The file is a sparse file.
        const FILE_ATTRIBUTE_SPARCE_FILE            = 0b0000_0000_0100_0000_0000_0000_0000_0000;

        /// The file or directory has an associated reparse point.
        const FILE_ATTRIBUTE_REPARSE_POINT          = 0b0000_0000_0010_0000_0000_0000_0000_0000;

        /// The file or directory is compressed. For a file, this means that all data in the file is compressed. For a directory, this means that compression is the default for newly created files and subdirectories.
        const FILE_ATTRIBUTE_COMPRESSED             = 0b0000_0000_0001_0000_0000_0000_0000_0000;

        /// The data of the file is not immediately available.
        const FILE_ATTRIBUTE_OFFLINE                = 0b0000_0000_0000_1000_0000_0000_0000_0000;

        /// The contents of the file need to be indexed.
        const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED    = 0b0000_0000_0000_0100_0000_0000_0000_0000;

        /// The file or directory is encrypted. For a file, this means that all data in the file is encrypted. For a directory, this means that encryption is the default for newly created files and subdirectories.
        const FILE_ATTRIBUTE_ENCRYPTED              = 0b0000_0000_0000_0010_0000_0000_0000_0000;

    }
}

bitflags! {
    /// A 32-bit unsigned integer that specifies the expected window state of an
    /// application launched by the link.
    pub struct ShowCommand: u32 {

        /// The application is open and its window is open in a normal fashion.
        const SW_SHOWNORMAL = 0x0000_0001;

        /// The application is open, and keyboard focus is given to the application, but its window is not shown.
        const SW_SHOWMAXIMIZED = 0x0000_0003;

        /// The application is open, but its window is not shown. It is not given the keyboard focus.
        const SW_SHOWMINNOACTIVE = 0x0000_0007;
    }
}

#[repr(C)]
#[derive(Clone, Copy, Debug)]
/// The HotKeyFlags structure specifies input generated by a combination of keyboard keys being
/// pressed.
pub struct HotKeyFlags {
    /// An 8-bit unsigned integer that specifies a virtual key code that corresponds to a
    /// key on the keyboard.
    pub low_byte: u8,

    /// An 8-bit unsigned integer that specifies bits that correspond to modifier keys on
    /// the keyboard.
    pub high_byte: u8,
}

impl From<u16> for HotKeyFlags {
    fn from(i: u16) -> Self {
        let mut cursor = Cursor::new(i.to_le_bytes());
        Self {
            low_byte: cursor.read_u8().unwrap(),
            high_byte: cursor.read_u8().unwrap(),
        }
    }
}

#[derive(Debug, Copy, Clone)]
/// Contains a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC).
pub struct FileTime {
    /// The low-order part of the file time.
    pub low: u32,

    /// The high-order part of the file time.
    pub high: u32,
}