winlnk 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README +26 -0
- data/Rakefile +7 -0
- data/lib/winlnk.rb +201 -0
- data/test/test_error.rb +42 -0
- metadata +49 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: d699909354195807a0b6d300658026db1e52e616
|
|
4
|
+
data.tar.gz: b8e3fd8963ef84caed6b84a7c63d403cb146e5b9
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 7280505f2676cc21b09add97b80cd3b1930e485868ac6eab1ec1aef497e2d257972a6794d200fa8423414defc79d4f4b014dd8dfee0055b746188807f0fd53c7
|
|
7
|
+
data.tar.gz: 9ef3ed9c34971b91ae4761981edea2ec100b3bbbc59cd9312bff95e98db13cc439bd3ae5d5ee56fc5372e3879d049a70158cc0418de24bf44bdc9da048c9c396
|
data/README
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Library to read Windows Shell Link (shortcut or .lnk) files
|
|
2
|
+
-----------------------------------------------------------
|
|
3
|
+
|
|
4
|
+
This is a library to parse Windows Shell Link (shortcut or .lnk) files
|
|
5
|
+
on non-Windows systems.
|
|
6
|
+
|
|
7
|
+
Installation
|
|
8
|
+
------------
|
|
9
|
+
|
|
10
|
+
With Gem:
|
|
11
|
+
|
|
12
|
+
# gem install winlnk
|
|
13
|
+
|
|
14
|
+
From the source:
|
|
15
|
+
|
|
16
|
+
% rake test
|
|
17
|
+
|
|
18
|
+
Copy the contents of the lib directory into somewhere you like.
|
|
19
|
+
|
|
20
|
+
Usage
|
|
21
|
+
-----
|
|
22
|
+
|
|
23
|
+
To read the link target, create an instance and get its path attribute.
|
|
24
|
+
|
|
25
|
+
link = WinLnk.new("path/to/link.lnk")
|
|
26
|
+
p link.path
|
data/Rakefile
ADDED
data/lib/winlnk.rb
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2014 KAMADA Ken'ichi.
|
|
3
|
+
# All rights reserved.
|
|
4
|
+
#
|
|
5
|
+
# Redistribution and use in source and binary forms, with or without
|
|
6
|
+
# modification, are permitted provided that the following conditions
|
|
7
|
+
# are met:
|
|
8
|
+
# 1. Redistributions of source code must retain the above copyright
|
|
9
|
+
# notice, this list of conditions and the following disclaimer.
|
|
10
|
+
# 2. Redistributions in binary form must reproduce the above copyright
|
|
11
|
+
# notice, this list of conditions and the following disclaimer in the
|
|
12
|
+
# documentation and/or other materials provided with the distribution.
|
|
13
|
+
#
|
|
14
|
+
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
15
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
16
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
17
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
18
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
19
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
20
|
+
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
21
|
+
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
22
|
+
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
23
|
+
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
24
|
+
# SUCH DAMAGE.
|
|
25
|
+
#
|
|
26
|
+
|
|
27
|
+
# This is a library to parse Windows Shell Link (shortcut or .lnk) files
|
|
28
|
+
# on non-Windows systems.
|
|
29
|
+
class WinLnk
|
|
30
|
+
MAGIC = "\x4c\x00\x00\x00".b
|
|
31
|
+
CLSID = "\x01\x14\x02\x00\x00\x00\x00\x00\xc0\x00\x00\x00\x00\x00\x00\x46".b
|
|
32
|
+
|
|
33
|
+
FLAG_HAS_LINK_TARGET_ID_LIST = 1 << 0
|
|
34
|
+
FLAG_HAS_LINK_INFO = 1 << 1
|
|
35
|
+
FLAG_HAS_NAME = 1 << 2
|
|
36
|
+
FLAG_HAS_RELATIVE_PATH = 1 << 3
|
|
37
|
+
FLAG_HAS_WORKING_DIR = 1 << 4
|
|
38
|
+
FLAG_HAS_ARGUMENTS = 1 << 5
|
|
39
|
+
FLAG_HAS_ICON_LOCATION = 1 << 6
|
|
40
|
+
FLAG_IS_UNICODE = 1 << 7
|
|
41
|
+
|
|
42
|
+
ATTR_READONLY = 1 << 0
|
|
43
|
+
ATTR_HIDDEN = 1 << 1
|
|
44
|
+
ATTR_SYSTEM = 1 << 2
|
|
45
|
+
ATTR_DIRECTORY = 1 << 4
|
|
46
|
+
ATTR_ARCHIVE = 1 << 5
|
|
47
|
+
ATTR_NORMAL = 1 << 7
|
|
48
|
+
ATTR_TEMPORARY = 1 << 8
|
|
49
|
+
ATTR_SPARSE_FILE = 1 << 9
|
|
50
|
+
ATTR_REPARSE_POINT = 1 << 10
|
|
51
|
+
ATTR_COMPRESSED = 1 << 11
|
|
52
|
+
ATTR_OFFLINE = 1 << 12
|
|
53
|
+
ATTR_NOT_CONTENT_INDEXED = 1 << 13
|
|
54
|
+
ATTR_ENCRYPTED = 1 << 14
|
|
55
|
+
|
|
56
|
+
SW_SHOWNORMAL = 1
|
|
57
|
+
SW_SHOWMAXIMIZED = 3
|
|
58
|
+
SW_SHOWMINNOACTIVE = 7
|
|
59
|
+
|
|
60
|
+
LI_FLAG_LOCAL = 1 << 0
|
|
61
|
+
LI_FLAG_NETWORK = 1 << 1
|
|
62
|
+
|
|
63
|
+
@@debug = nil
|
|
64
|
+
|
|
65
|
+
# Returns the LinkFlags of the link.
|
|
66
|
+
attr_reader :flags
|
|
67
|
+
# Returns the path pointed to by the link.
|
|
68
|
+
attr_reader :path
|
|
69
|
+
# Returns the description of the link.
|
|
70
|
+
attr_reader :description
|
|
71
|
+
# Returns the path of the link target relative to the link.
|
|
72
|
+
attr_reader :relative_path
|
|
73
|
+
# Returns the working directory used when activating the link target.
|
|
74
|
+
attr_reader :working_directory
|
|
75
|
+
# Returns the command-line arguments specified when activating
|
|
76
|
+
# the link target.
|
|
77
|
+
attr_reader :arguments
|
|
78
|
+
# Returns the location of the icon.
|
|
79
|
+
attr_reader :icon_location
|
|
80
|
+
|
|
81
|
+
# Parses a shell link file given by +pathname+ and returns
|
|
82
|
+
# a +WinLnk+ object. The encoding of non-Unicode strings is assumed
|
|
83
|
+
# to be +codepage+.
|
|
84
|
+
def initialize(pathname, codepage)
|
|
85
|
+
@codepage = codepage
|
|
86
|
+
@data = open(pathname, "rb:ASCII-8BIT") { |f| f.read }
|
|
87
|
+
off = read_header()
|
|
88
|
+
printf("Link flags: %b\n", @flags) if @@debug
|
|
89
|
+
|
|
90
|
+
if @flags & FLAG_HAS_LINK_TARGET_ID_LIST != 0
|
|
91
|
+
off = read_id_list(off)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
if @flags & FLAG_HAS_LINK_INFO != 0
|
|
95
|
+
off = read_link_info(off)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
if @flags & FLAG_HAS_NAME != 0
|
|
99
|
+
@description, off = read_string(off)
|
|
100
|
+
end
|
|
101
|
+
if @flags & FLAG_HAS_RELATIVE_PATH != 0
|
|
102
|
+
@relative_path, off = read_string(off)
|
|
103
|
+
end
|
|
104
|
+
if @flags & FLAG_HAS_WORKING_DIR != 0
|
|
105
|
+
@working_directory, off = read_string(off)
|
|
106
|
+
end
|
|
107
|
+
if @flags & FLAG_HAS_ARGUMENTS != 0
|
|
108
|
+
@arguments, off = read_string(off)
|
|
109
|
+
end
|
|
110
|
+
if @flags & FLAG_HAS_ICON_LOCATION != 0
|
|
111
|
+
@icon_location, off = read_string(off)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
remove_instance_variable(:@data)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
private
|
|
118
|
+
|
|
119
|
+
def read_header()
|
|
120
|
+
raise ParseError.new("Not a shell link file") if data(0x00, 4) != MAGIC
|
|
121
|
+
raise ParseError.new("CLSID mismatch") if data(0x04, 16) != CLSID
|
|
122
|
+
@flags, @attributes = data(0x14, 8).unpack("V2")
|
|
123
|
+
times = data(0x1c, 24).unpack("V6")
|
|
124
|
+
@btime = filetime2posixtime(times[1] << 32 | times[0])
|
|
125
|
+
@atime = filetime2posixtime(times[3] << 32 | times[2])
|
|
126
|
+
@mtime = filetime2posixtime(times[5] << 32 | times[4])
|
|
127
|
+
@file_size, @icon_index, @show_cmd, @hot_key = data(0x34, 16).unpack("V3v")
|
|
128
|
+
reserved = data(0x44, 8).unpack("vV2")
|
|
129
|
+
return 0x4c
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def filetime2posixtime(filetime)
|
|
133
|
+
# Windows FILETIME is the time from 1601-01-01 in 100-nanosecond unit.
|
|
134
|
+
filetime -= 116444736000000000
|
|
135
|
+
return Time.at(filetime / 10000000, filetime % 10000000 / 10)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def read_id_list(off)
|
|
139
|
+
@id_list = []
|
|
140
|
+
len, = data(off, 2).unpack("v")
|
|
141
|
+
off += 2
|
|
142
|
+
nextoff = off + len
|
|
143
|
+
loop do
|
|
144
|
+
itemlen, = data(off, 2).unpack("v")
|
|
145
|
+
return nextoff if itemlen == 0
|
|
146
|
+
@id_list.push(data(off + 2, itemlen - 2))
|
|
147
|
+
off += itemlen
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def read_link_info(off)
|
|
152
|
+
len, = data(off, 4).unpack("V")
|
|
153
|
+
raise ParseError.new("Too short LinkInfo") if len < 0x1c
|
|
154
|
+
header_len, li_flags, vol_id_off, base_path_off,
|
|
155
|
+
net_rel_link_off, suffix_off = data(off + 4, 24).unpack("V6")
|
|
156
|
+
if @@debug
|
|
157
|
+
printf("LinkInfo header size: %u\n", header_len)
|
|
158
|
+
printf("LinkInfo flags: %b\n", li_flags)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
if li_flags & LI_FLAG_LOCAL != 0
|
|
162
|
+
base_path, = data_all(off + base_path_off).unpack("Z*")
|
|
163
|
+
suffix, = data_all(off + suffix_off).unpack("Z*")
|
|
164
|
+
@path = (base_path + suffix).force_encoding(@codepage)
|
|
165
|
+
end
|
|
166
|
+
if li_flags & LI_FLAG_NETWORK != 0
|
|
167
|
+
# Parse the CommonNetworkRelativeLink structure.
|
|
168
|
+
net_name_off, = data(off + net_rel_link_off + 8, 2).unpack("v")
|
|
169
|
+
net_name, = data_all(off + net_rel_link_off + net_name_off).unpack("Z*")
|
|
170
|
+
suffix, = data_all(off + suffix_off).unpack("Z*")
|
|
171
|
+
@path = (net_name + "\\" + suffix).force_encoding(@codepage)
|
|
172
|
+
end
|
|
173
|
+
return off + len
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def read_string(off)
|
|
177
|
+
len, = data(off, 2).unpack("v")
|
|
178
|
+
if @flags & FLAG_IS_UNICODE != 0
|
|
179
|
+
# UTF-16.
|
|
180
|
+
len *= 2
|
|
181
|
+
return data(off + 2, len).encode("UTF-8", "UTF-16LE"), off + len + 2
|
|
182
|
+
else
|
|
183
|
+
# The system default code page.
|
|
184
|
+
return data(off + 2, len).force_encoding(@codepage), off + len + 2
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def data(off, len)
|
|
189
|
+
raise ParseError.new("Truncated file") if @data.size < off + len
|
|
190
|
+
return @data[off, len]
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def data_all(off)
|
|
194
|
+
raise ParseError.new("Truncated file") if @data.size < off
|
|
195
|
+
return @data[off..-1]
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# This exception is raised when failed to parse a link.
|
|
199
|
+
class ParseError < StandardError
|
|
200
|
+
end
|
|
201
|
+
end
|
data/test/test_error.rb
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2015 KAMADA Ken'ichi.
|
|
3
|
+
# All rights reserved.
|
|
4
|
+
#
|
|
5
|
+
# Redistribution and use in source and binary forms, with or without
|
|
6
|
+
# modification, are permitted provided that the following conditions
|
|
7
|
+
# are met:
|
|
8
|
+
# 1. Redistributions of source code must retain the above copyright
|
|
9
|
+
# notice, this list of conditions and the following disclaimer.
|
|
10
|
+
# 2. Redistributions in binary form must reproduce the above copyright
|
|
11
|
+
# notice, this list of conditions and the following disclaimer in the
|
|
12
|
+
# documentation and/or other materials provided with the distribution.
|
|
13
|
+
#
|
|
14
|
+
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
15
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
16
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
17
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
18
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
19
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
20
|
+
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
21
|
+
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
22
|
+
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
23
|
+
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
24
|
+
# SUCH DAMAGE.
|
|
25
|
+
#
|
|
26
|
+
|
|
27
|
+
require "test/unit"
|
|
28
|
+
require "winlnk"
|
|
29
|
+
|
|
30
|
+
class TestError < Test::Unit::TestCase
|
|
31
|
+
def test_enoent
|
|
32
|
+
assert_raise(Errno::ENOENT) do
|
|
33
|
+
WinLnk.new("no_such_file", "US-ASCII")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_not_lnk
|
|
38
|
+
assert_raise(WinLnk::ParseError) do
|
|
39
|
+
WinLnk.new("test/test_error.rb", "US-ASCII")
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: winlnk
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- KAMADA Ken'ichi
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2017-04-26 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: |
|
|
14
|
+
This is a library to parse Windows Shell Link (shortcut or .lnk) files
|
|
15
|
+
on non-Windows systems.
|
|
16
|
+
email: kamada@nanohz.org
|
|
17
|
+
executables: []
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- README
|
|
22
|
+
- Rakefile
|
|
23
|
+
- lib/winlnk.rb
|
|
24
|
+
- test/test_error.rb
|
|
25
|
+
homepage: https://github.com/kamadak/winlnk-rb
|
|
26
|
+
licenses:
|
|
27
|
+
- BSD-2-Clause
|
|
28
|
+
metadata: {}
|
|
29
|
+
post_install_message:
|
|
30
|
+
rdoc_options: []
|
|
31
|
+
require_paths:
|
|
32
|
+
- lib
|
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
34
|
+
requirements:
|
|
35
|
+
- - ">="
|
|
36
|
+
- !ruby/object:Gem::Version
|
|
37
|
+
version: '0'
|
|
38
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
39
|
+
requirements:
|
|
40
|
+
- - ">="
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '0'
|
|
43
|
+
requirements: []
|
|
44
|
+
rubyforge_project:
|
|
45
|
+
rubygems_version: 2.5.2
|
|
46
|
+
signing_key:
|
|
47
|
+
specification_version: 4
|
|
48
|
+
summary: Library to read Windows Shell Link (shortcut or .lnk) files
|
|
49
|
+
test_files: []
|