ipcam 0.3.5 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 364d7aea8083301b5c3bfd0ec5c26d8afa68ac6496433495da0e56812f4dafe0
4
- data.tar.gz: '09776c05b93354b60adaf89314d3c4f0ba1878ef83bd4cef1410cc72ba0639bb'
3
+ metadata.gz: a3646608decd74c82db0631040a7ddd1b4f94ae1feef0ae39272505a4b27e45f
4
+ data.tar.gz: bf43c0a17f8e99a083776aa4e983ab86525dc1cdf0f3949588549f54dd2662af
5
5
  SHA512:
6
- metadata.gz: 983cdf9103d28102f42f76f8b230b84c5ece86c41f94d01c78cce4a1d97461e7ff25c60eb344f508e107a338a3fedcd9eb880b11fe9712a121692580b33cb549
7
- data.tar.gz: eb89a8b0d1ad4e512f764e292c9fb61b237571bfc4bf4d785db335020704d58fe025ea0257f090911cd21fde6007c26423d74a9fe77f787bb2032ab326df33c0
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
  #
@@ -1,3 +1,3 @@
1
1
  module IPCam
2
- VERSION = "0.3.5"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -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.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_QUICKACK, 1)
97
- sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
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, body)
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
- return "tcp://#{addr}:#{$http_port}"
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
 
@@ -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
- EM::WebSocket.start(:host => $bind_addr, :port => $ws_port) { |sock|
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]
@@ -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(WS_URL);
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.3.5
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-06-27 00:00:00.000000000 Z
11
+ date: 2019-10-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler