ipcam 0.3.5 → 0.4.0
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 +4 -4
- data/README.md +47 -0
- data/bin/ipcam +50 -0
- data/lib/ipcam/version.rb +1 -1
- data/lib/ipcam/webserver.rb +42 -9
- data/lib/ipcam/websock.rb +12 -2
- data/resource/ipcam/js/main.js +1 -3
- data/resource/ipcam/scss/main/style.scss +12 -0
- data/resource/ipcam/views/main.erb +5 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3646608decd74c82db0631040a7ddd1b4f94ae1feef0ae39272505a4b27e45f
|
4
|
+
data.tar.gz: bf43c0a17f8e99a083776aa4e983ab86525dc1cdf0f3949588549f54dd2662af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 56806547cb2de27720261e2c026337709e505f05ac8c73a9581f2d692a81af66c1358b4b99cc174e9035243e60aaaeecff87fb4b2ab080f5c6d75f8a1995536b
|
7
|
+
data.tar.gz: 15787dd7f41e5a406e50d8b9ffc4c6fc3802e677b1680a3aa5b9e4d29bb711c4af91495a28e2c6465aca9cc65f03fc4f6afc7ac90e69b7bf8a4d4642c1d1efb6
|
data/README.md
CHANGED
@@ -22,6 +22,11 @@ Connect a camera device compatible with V4L2 and start ipcam as follows.
|
|
22
22
|
```
|
23
23
|
ipcam [options] [device-file]
|
24
24
|
options:
|
25
|
+
--use-ssl
|
26
|
+
--ssl-cert=CRT-FILE
|
27
|
+
--ssl-key=KEY-FILE
|
28
|
+
-D, --digest-auth=FILE
|
29
|
+
-A, --add-user=USER,PASSWD
|
25
30
|
--bind=ADDR
|
26
31
|
--port=PORT
|
27
32
|
-d, --database-file=FILE
|
@@ -40,6 +45,21 @@ Then connect to port 4567 by http browser and operate. The accessible URLs are a
|
|
40
45
|
|
41
46
|
### options
|
42
47
|
<dl>
|
48
|
+
<dt>--use-ssl</dt>
|
49
|
+
<dd>Specify use SSL/TLS. If you use this option, shall specify a certificate file and a key file (Use the --ssl-cert and --ssl-key options).</dd>
|
50
|
+
|
51
|
+
<dt>--ssl-cert=CRT-FILE</dt>
|
52
|
+
<dd>Specifies the file that contains the X 509 server certificate.</dd>
|
53
|
+
|
54
|
+
<dt>--ssl-key=KEY-FILE</dt>
|
55
|
+
<dd>Specifies the key file that was used when the certificate was created.</dd>
|
56
|
+
|
57
|
+
<dt>-D, --digest-auth=YAML-FILE</dt>
|
58
|
+
<dd>Specifies to use restrict access by Digest Authentication. This argument is followed by a password file written in YAML.</dd>
|
59
|
+
|
60
|
+
<dt>-A, --add-user=USER-NAME,PASSWORD</dt>
|
61
|
+
<dd>Add entry to the password file. If you specify this option, only to add an entry to the password file to exit this application.</dd>
|
62
|
+
|
43
63
|
<dt>--bind=ADDR</dt>
|
44
64
|
<dd>Specify the address to which the HTTP server binds. by default, IPv6 any address("::") is used.</dd>
|
45
65
|
|
@@ -62,6 +82,33 @@ Then connect to port 4567 by http browser and operate. The accessible URLs are a
|
|
62
82
|
<dd></dd>
|
63
83
|
</dl>
|
64
84
|
|
85
|
+
### Use digest authentication
|
86
|
+
To restrict access by digest authentication, the password file written in YAML must be specified in the "--digest-auth" option. This file must be YAML-encoded map data of A1 strings (ref. RFC 7616) keyed by the user name.
|
87
|
+
|
88
|
+
This file can be created using the "--add-user" option. The actual procedure is as follows.
|
89
|
+
|
90
|
+
#### Create password file
|
91
|
+
##### create password file, and add user "foo"
|
92
|
+
If specified password file does not exist and the "--digest-auth" and "--add-user" options are specified together, new password file containing user entry will be created.
|
93
|
+
```
|
94
|
+
ipcam --digest-auth passwd.yml --add-user foo,XXXXXXX
|
95
|
+
```
|
96
|
+
|
97
|
+
##### and add user "bar"
|
98
|
+
If specified password file exists and the "--digest-auth" option and "--add-user" option are specified together, a user entry is added to the password file.
|
99
|
+
```
|
100
|
+
ipcam --digest-auth passwd.yml --add-user bar,YYYYYY
|
101
|
+
```
|
102
|
+
|
103
|
+
#### Run the server
|
104
|
+
If only the "--digest-auth" option is specified, the server is started and performs digest authentication with the specified password file.
|
105
|
+
```
|
106
|
+
ipcam --digest-auth passwd.yml --use-ssl --ssl-cert cert/server.crt --ssl-key cert/server.key /dev/video0
|
107
|
+
```
|
108
|
+
|
109
|
+
#### Delete user from password file
|
110
|
+
To delete a user, edit the YAML file directly.
|
111
|
+
|
65
112
|
### device-file
|
66
113
|
specify target device file (ex: /dev/video1). if omittedm, it will use "/dev/video0".
|
67
114
|
|
data/bin/ipcam
CHANGED
@@ -10,6 +10,8 @@
|
|
10
10
|
require 'pathname'
|
11
11
|
require 'optparse'
|
12
12
|
require 'logger'
|
13
|
+
require 'yaml'
|
14
|
+
require 'digest/md5'
|
13
15
|
|
14
16
|
Thread.abort_on_exception = true
|
15
17
|
|
@@ -47,6 +49,49 @@ OptionParser.new { |opt|
|
|
47
49
|
opt.version = IPCam::VERSION
|
48
50
|
opt.banner += " [DEVICE-FILE]"
|
49
51
|
|
52
|
+
opt.on('--use-ssl') {
|
53
|
+
$use_ssl = true
|
54
|
+
}
|
55
|
+
|
56
|
+
opt.on('--ssl-cert=CRT-FILE', String) { |val|
|
57
|
+
$ssl_cert = val
|
58
|
+
}
|
59
|
+
|
60
|
+
opt.on('--ssl-key=KEY-FILE', String) { |val|
|
61
|
+
$ssl_key = val
|
62
|
+
}
|
63
|
+
|
64
|
+
opt.on('-D', '--digest-auth=FILE', String) { |val|
|
65
|
+
$use_dauth = true
|
66
|
+
$pwd_file = Pathname.new(val)
|
67
|
+
|
68
|
+
if $pwd_file.exist?
|
69
|
+
if $pwd_file.world_readable? || $pwd_file.world_writable?
|
70
|
+
raise("password file shall be not world readble/writable")
|
71
|
+
end
|
72
|
+
|
73
|
+
$pwd_db = YAML.load_file(val)
|
74
|
+
else
|
75
|
+
|
76
|
+
$pwd_db = {}
|
77
|
+
end
|
78
|
+
}
|
79
|
+
|
80
|
+
opt.on('-A', '--add-user=USER,PASSWD', Array) { |val|
|
81
|
+
raise("passwd file is not specified") if not $pwd_db
|
82
|
+
raise("user \"#{val[0]}\" is already exist") if $pwd_db.include?(val[0])
|
83
|
+
|
84
|
+
$pwd_db[val[0]] = \
|
85
|
+
Digest::MD5.hexdigest("#{val[0]}:#{TRADITIONAL_NAME}:#{val[1]}")
|
86
|
+
|
87
|
+
$pwd_file.open("w") { |f|
|
88
|
+
f.chmod(0o600)
|
89
|
+
f.write($pwd_db.to_yaml)
|
90
|
+
}
|
91
|
+
|
92
|
+
exit
|
93
|
+
}
|
94
|
+
|
50
95
|
opt.on('--bind=ADDR') { |val|
|
51
96
|
$bind_addr = val
|
52
97
|
}
|
@@ -117,6 +162,11 @@ OptionParser.new { |opt|
|
|
117
162
|
if $db_file.exist? and (not $db_file.writable?)
|
118
163
|
raise("#{$db_file.to_s} is not writable")
|
119
164
|
end
|
165
|
+
|
166
|
+
if $use_ssl
|
167
|
+
raise("SSL cert file not specified") if not $ssl_cert
|
168
|
+
raise("SSL key file not specified") if not $ssl_key
|
169
|
+
end
|
120
170
|
}
|
121
171
|
|
122
172
|
#
|
data/lib/ipcam/version.rb
CHANGED
data/lib/ipcam/webserver.rb
CHANGED
@@ -50,12 +50,24 @@ module IPCam
|
|
50
50
|
|
51
51
|
return nil
|
52
52
|
end
|
53
|
+
|
54
|
+
def websock_url
|
55
|
+
return "#{($use_ssl)? "wss":"ws"}://${location.hostname}:#{$ws_port}"
|
56
|
+
end
|
53
57
|
end
|
54
58
|
|
55
59
|
get "/" do
|
56
60
|
redirect "/main"
|
57
61
|
end
|
58
62
|
|
63
|
+
get "/js/const.js" do
|
64
|
+
content_type("text/javascript")
|
65
|
+
|
66
|
+
<<~EOS
|
67
|
+
const WEBSOCK_URL = `#{websock_url}`;
|
68
|
+
EOS
|
69
|
+
end
|
70
|
+
|
59
71
|
get "/main" do
|
60
72
|
erb :main
|
61
73
|
end
|
@@ -93,8 +105,10 @@ module IPCam
|
|
93
105
|
begin
|
94
106
|
app.add_client(queue)
|
95
107
|
|
96
|
-
sock.
|
97
|
-
|
108
|
+
if sock.kind_of?(TCPSocket)
|
109
|
+
sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_QUICKACK, 1)
|
110
|
+
sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
111
|
+
end
|
98
112
|
|
99
113
|
sock.write("\r\n".b)
|
100
114
|
sock.flush
|
@@ -107,23 +121,24 @@ module IPCam
|
|
107
121
|
|
108
122
|
if $extend_header
|
109
123
|
header = <<~EOT.b
|
110
|
-
--#{boundary}
|
124
|
+
--#{boundary}\r
|
111
125
|
Content-Type: image/jpeg\r
|
112
126
|
Content-Length: #{body.bytesize}\r
|
113
|
-
X-Frame-Number: #{fc}
|
114
|
-
X-Timestamp: #{(Time.now.to_f * 1000).round}
|
127
|
+
X-Frame-Number: #{fc}\r
|
128
|
+
X-Timestamp: #{(Time.now.to_f * 1000).round}\r
|
115
129
|
\r
|
116
130
|
EOT
|
117
131
|
else
|
118
132
|
header = <<~EOT.b
|
119
|
-
--#{boundary}
|
133
|
+
--#{boundary}\r
|
120
134
|
Content-Type: image/jpeg\r
|
121
135
|
Content-Length: #{body.bytesize}\r
|
122
136
|
\r
|
123
137
|
EOT
|
124
138
|
end
|
125
139
|
|
126
|
-
sock.write(header
|
140
|
+
sock.write(header)
|
141
|
+
sock.write(body)
|
127
142
|
sock.flush
|
128
143
|
fc += 1
|
129
144
|
|
@@ -143,7 +158,7 @@ module IPCam
|
|
143
158
|
}
|
144
159
|
}
|
145
160
|
|
146
|
-
# ↓力業でsinatraがContent-Length
|
161
|
+
# ↓力業でsinatraがContent-Lengthを付与するのを抑止している
|
147
162
|
def response.calculate_content_length?
|
148
163
|
return false
|
149
164
|
end
|
@@ -167,6 +182,18 @@ module IPCam
|
|
167
182
|
end
|
168
183
|
|
169
184
|
class << self
|
185
|
+
if $use_dauth
|
186
|
+
def new(*)
|
187
|
+
ret = Rack::Auth::Digest::MD5.new(super) {|user| $pwd_db[user]}
|
188
|
+
|
189
|
+
ret.realm = TRADITIONAL_NAME
|
190
|
+
ret.opaque = SecureRandom.alphanumeric(32)
|
191
|
+
ret.passwords_hashed = true
|
192
|
+
|
193
|
+
return ret
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
170
197
|
def bind_url
|
171
198
|
if $bind_addr.include?(":")
|
172
199
|
addr = "[#{$bind_addr}]" if $bind_addr.include?(":")
|
@@ -174,7 +201,13 @@ module IPCam
|
|
174
201
|
addr = $bind_addr
|
175
202
|
end
|
176
203
|
|
177
|
-
|
204
|
+
if $use_ssl
|
205
|
+
ret = "ssl://#{addr}:#{$http_port}?key=#{$ssl_key}&cert=#{$ssl_cert}"
|
206
|
+
else
|
207
|
+
ret = "tcp://#{addr}:#{$http_port}"
|
208
|
+
end
|
209
|
+
|
210
|
+
return ret
|
178
211
|
end
|
179
212
|
private :bind_url
|
180
213
|
|
data/lib/ipcam/websock.rb
CHANGED
@@ -106,7 +106,7 @@ module IPCam
|
|
106
106
|
addr = $bind_addr
|
107
107
|
end
|
108
108
|
|
109
|
-
return "tcp://#{addr}:#{$ws_port}"
|
109
|
+
return "#{($use_ssl)? "ssl":"tcp"}://#{addr}:#{$ws_port}"
|
110
110
|
end
|
111
111
|
private :bind_url
|
112
112
|
|
@@ -121,7 +121,17 @@ module IPCam
|
|
121
121
|
|
122
122
|
$logger.info("websock") {"started (#{bind_url()})"}
|
123
123
|
|
124
|
-
|
124
|
+
opts = {
|
125
|
+
:host => $bind_addr,
|
126
|
+
:port => $ws_port,
|
127
|
+
:secure => $use_ssl,
|
128
|
+
:tls_options => {
|
129
|
+
:private_key_file => $ssl_key,
|
130
|
+
:cert_chain_file => $ssl_cert
|
131
|
+
}
|
132
|
+
}
|
133
|
+
|
134
|
+
EM::WebSocket.start(opts) { |sock|
|
125
135
|
peer = Socket.unpack_sockaddr_in(sock.get_peername)
|
126
136
|
addr = peer[1]
|
127
137
|
port = peer[0]
|
data/resource/ipcam/js/main.js
CHANGED
@@ -9,8 +9,6 @@
|
|
9
9
|
* define constants
|
10
10
|
*/
|
11
11
|
|
12
|
-
const WS_URL = `ws://${location.hostname}:${parseInt(location.port)+1}/`;
|
13
|
-
|
14
12
|
/*
|
15
13
|
* declar package global variabled
|
16
14
|
*/
|
@@ -547,7 +545,7 @@
|
|
547
545
|
}
|
548
546
|
|
549
547
|
function initialize() {
|
550
|
-
session = new Session(
|
548
|
+
session = new Session(WEBSOCK_URL);
|
551
549
|
capabilities = null;
|
552
550
|
controls = null;
|
553
551
|
sliders = null;
|
@@ -18,6 +18,7 @@ body {
|
|
18
18
|
}
|
19
19
|
|
20
20
|
div.jumbotron {
|
21
|
+
position: relative;
|
21
22
|
padding: 2rem 1rem;
|
22
23
|
margin-bottom: 0px;
|
23
24
|
|
@@ -30,6 +31,17 @@ body {
|
|
30
31
|
}
|
31
32
|
}
|
32
33
|
|
34
|
+
div#version {
|
35
|
+
position: absolute;
|
36
|
+
right: 5px;
|
37
|
+
bottom: 5px;
|
38
|
+
opacity: 0.0;
|
39
|
+
}
|
40
|
+
|
41
|
+
div#version:hover {
|
42
|
+
opacity: 1.0;
|
43
|
+
}
|
44
|
+
|
33
45
|
div#switches {
|
34
46
|
width: 15%;
|
35
47
|
height: 100%;
|
@@ -11,6 +11,7 @@
|
|
11
11
|
</meta>
|
12
12
|
|
13
13
|
<script type="text/javascript" src="/js/jquery-3.4.1.min.js"></script>
|
14
|
+
<script type="text/javascript" src="/js/const.js"></script>
|
14
15
|
<script type="text/javascript" src="/js/util.js"></script>
|
15
16
|
<script type="text/javascript" src="/js/main.js"></script>
|
16
17
|
</head>
|
@@ -21,6 +22,10 @@
|
|
21
22
|
<h3 id="device-file"></h3>
|
22
23
|
<h6 id="device-name"></h3>
|
23
24
|
</div>
|
25
|
+
|
26
|
+
<div id="version">
|
27
|
+
version <%= IPCam::VERSION %>
|
28
|
+
</div>
|
24
29
|
</div>
|
25
30
|
|
26
31
|
<div id="main-area" class="d-flex d-flex-row">
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ipcam
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hirosho Kuwagata
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-10-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|