1 <!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML
4.0//EN'
>
6 For use with Tomato Firmware only.
7 No part of this file may be used without permission.
11 <meta http-equiv='content-type' content='text/html;charset=utf-
8'
>
12 <meta name='robots' content='noindex,nofollow'
>
13 <title>[<%
ident(); %>] NAS: FTP Server
</title>
14 <link rel='stylesheet' type='text/css' href='tomato.css'
>
15 <link rel='stylesheet' type='text/css' href='color.css'
>
16 <script type='text/javascript' src='tomato.js'
></script>
19 <style tyle='text/css'
>
32 <style type='text/css'
>
39 <script type='text/javascript' src='debug.js'
></script>
41 <script type='text/javascript'
>
43 // <% nvram("ftp_enable,ftp_super,ftp_anonymous,ftp_dirlist,ftp_port,ftp_max,ftp_ipmax,ftp_staytimeout,ftp_rate,ftp_anonrate,ftp_anonroot,ftp_pubroot,ftp_pvtroot,ftp_custom,ftp_users,ftp_sip,ftp_limit,log_ftp"); %>
45 ftplimit
= nvram
.ftp_limit
.split(',');
46 if (ftplimit
.length
!= 3) ftplimit
= [0,3,60];
48 var aftg
= new TomatoGrid();
50 aftg
.exist = function(f
, v
)
52 var data
= this.getAllData();
53 for (var i
= 0; i
< data
.length
; ++i
) {
54 if (data
[i
][f
] == v
) return true;
59 aftg
.existName = function(name
)
61 return this.exist(0, name
);
64 aftg
.sortCompare = function(a
, b
) {
65 var da
= a
.getRowData();
66 var db
= b
.getRowData();
67 var r
= cmpText(da
[this.sortColumn
], db
[this.sortColumn
]);
68 return this.sortAscending
? r
: -r
;
71 aftg
.verifyFields = function(row
, quiet
)
74 f
= fields
.getAll(row
);
79 if (!v_length(f
[0], quiet
, 1)) return 0;
81 s
= f
[0].value
.trim().replace(/\s+/g, ' ');
83 if (s
.search(/^[a-zA-Z0-9_\-]+$/) == -1) {
84 ferror
.set(f
[0], 'Invalid user name. Only characters "A-Z 0-9 - _" are allowed.', quiet
);
87 if (this.existName(s
)) {
88 ferror
.set(f
[0], 'Duplicate user name.', quiet
);
91 if (s
== 'root' || s
== 'admin') {
92 ferror
.set(f
[0], 'User names "root" and "admin" are not allowed.', quiet
);
98 if (!v_length(f
[1], quiet
, 1)) return 0;
99 if (!v_nodelim(f
[1], quiet
, 'Password', 1)) return 0;
104 aftg
.resetNewEditor = function() {
107 f
= fields
.getAll(this.newEditor
);
112 f
[2].selectedIndex
= 0;
115 aftg
.setup = function()
117 this.init('aft-grid', 'sort', 50, [
118 { type
: 'text', maxlen
: 50 },
119 { type
: 'password', maxlen
: 50, peekaboo
: 1 },
120 { type
: 'select', options
: [['Read/Write', 'Read/Write'],['Read Only', 'Read Only'],['View Only', 'View Only'],['Private', 'Private']] }
122 this.headerSet(['User Name', 'Password', 'Access']);
124 var s
= nvram
.ftp_users
.split('>');
125 for (var i
= 0; i
< s
.length
; ++i
) {
126 var t
= s
[i
].split('<');
128 this.insertData(-1, t
);
132 this.showNewEditor();
133 this.resetNewEditor();
136 function verifyFields(focused
, quiet
)
141 a
= E('_ftp_enable').value
;
143 elem
.display(PR(b
), (a
!= 0));
145 elem
.display(PR(b
), (a
== 1));
147 E('_f_limit').disabled
= (a
!= 1);
148 b
= E('_f_limit').checked
;
149 elem
.display(PR('_f_limit_hit'), PR('_f_limit_sec'), (a
== 1 && b
));
150 E('_ftp_anonymous').disabled
= (a
== 0);
151 E('_f_ftp_super').disabled
= (a
== 0);
152 E('_f_log_ftp').disabled
= (a
== 0);
153 E('_ftp_pubroot').disabled
= (a
== 0);
154 E('_ftp_pvtroot').disabled
= (a
== 0);
155 E('_ftp_anonroot').disabled
= (a
== 0);
156 E('_ftp_dirlist').disabled
= (a
== 0);
157 E('_ftp_max').disabled
= (a
== 0);
158 E('_ftp_ipmax').disabled
= (a
== 0);
159 E('_ftp_rate').disabled
= (a
== 0);
160 E('_ftp_anonrate').disabled
= (a
== 0);
161 E('_ftp_staytimeout').disabled
= (a
== 0);
162 E('_ftp_custom').disabled
= (a
== 0);
165 if (!v_port('_ftp_port', quiet
|| !ok
)) ok
= 0;
166 if (!v_range('_ftp_max', quiet
|| !ok
, 0, 12)) ok
= 0;
167 if (!v_range('_ftp_ipmax', quiet
|| !ok
, 0, 12)) ok
= 0;
168 if (!v_range('_ftp_rate', quiet
|| !ok
, 0, 99999)) ok
= 0;
169 if (!v_range('_ftp_anonrate', quiet
|| !ok
, 0, 99999)) ok
= 0;
170 if (!v_range('_ftp_staytimeout', quiet
|| !ok
, 0, 65535)) ok
= 0;
171 if (!v_length('_ftp_custom', quiet
|| !ok
, 0, 2048)) ok
= 0;
172 if (!v_path('_ftp_pubroot', quiet
|| !ok
, 0)) ok
= 0;
173 if (!v_path('_ftp_pvtroot', quiet
|| !ok
, 0)) ok
= 0;
174 if (!v_path('_ftp_anonroot', quiet
|| !ok
, 0)) ok
= 0;
176 if (!v_range('_f_limit_hit', quiet
|| !ok
, 1, 100)) ok
= 0;
177 if (!v_range('_f_limit_sec', quiet
|| !ok
, 3, 3600)) ok
= 0;
183 if ((b
.value
.length
) && (!_v_iptaddr(b
, quiet
|| !ok
, 15, 1, 1))) ok
= 0;
184 else ferror
.clear(b
);
192 if (aftg
.isEditing()) return;
193 if (!verifyFields(null, 0)) return;
197 var data
= aftg
.getAllData();
199 for (var i
= 0; i
< data
.length
; ++i
) r
.push(data
[i
].join('<'));
200 fom
.ftp_users
.value
= r
.join('>');
202 fom
.ftp_sip
.value
= fom
.f_ftp_sip
.value
.split(/\s*,\s*/).join(',');
203 fom
.ftp_super
.value
= E('_f_ftp_super').checked
? 1 : 0;
204 fom
.log_ftp
.value
= E('_f_log_ftp').checked
? 1 : 0;
206 fom
.ftp_limit
.value
= (E('_f_limit').checked
? 1 : 0) +
207 ',' + E('_f_limit_hit').value
+ ',' + E('_f_limit_sec').value
;
215 <form id='_fom' method='post' action='tomato.cgi'
>
216 <table id='container' cellspacing=
0>
217 <tr><td colspan=
2 id='header'
>
218 <div class='title'
>Tomato
</div>
219 <div class='version'
>Version <%
version(); %></div>
221 <tr id='body'
><td id='navi'
><script type='text/javascript'
>navi()</script></td>
223 <div id='ident'
><%
ident(); %></div>
227 <input type='hidden' name='_nextpage' value='nas-ftp.asp'
>
228 <input type='hidden' name='_service' value='ftpd-restart'
>
230 <input type='hidden' name='ftp_super'
>
231 <input type='hidden' name='log_ftp'
>
232 <input type='hidden' name='ftp_users'
>
233 <input type='hidden' name='ftp_sip'
>
234 <input type='hidden' name='ftp_limit'
>
236 <div class='section-title'
>FTP Server Configuration
</div>
237 <div class='section'
>
238 <script type='text/javascript'
>
239 createFieldTable('', [
240 { title
: 'Enable FTP Server', name
: 'ftp_enable', type
: 'select',
241 options
: [['0', 'No'],['1', 'Yes, WAN and LAN'],['2', 'Yes, LAN only']],
242 value
: nvram
.ftp_enable
},
243 { title
: 'FTP Port', indent
: 2, name
: 'ftp_port', type
: 'text', maxlen
: 5, size
: 7, value
: fixPort(nvram
.ftp_port
, 21) },
244 { title
: 'Allowed Remote<br>Address(es)', indent
: 2, name
: 'f_ftp_sip', type
: 'text', maxlen
: 512, size
: 64, value
: nvram
.ftp_sip
,
245 suffix
: '<br><small>(optional; ex: "1.1.1.1", "1.1.1.0/24", "1.1.1.1 - 2.2.2.2" or "me.example.com")</small>' },
246 { title
: 'Anonymous Users Access', name
: 'ftp_anonymous', type
: 'select',
247 options
: [['0', 'Disabled'],['1', 'Read/Write'],['2', 'Read Only'],['3', 'Write Only']],
248 value
: nvram
.ftp_anonymous
},
249 { title
: 'Allow Admin Login*', name
: 'f_ftp_super', type
: 'checkbox',
250 suffix
: ' <small>Allows users to connect with admin account.</small>',
251 value
: nvram
.ftp_super
== 1 },
252 { title
: 'Log FTP requests and responses', name
: 'f_log_ftp', type
: 'checkbox',
253 value
: nvram
.log_ftp
== 1 }
256 <small><br>*
Avoid using this option when FTP server is enabled for WAN. IT PROVIDES FULL ACCESS TO THE ROUTER FILE SYSTEM!
</small>
259 <div class='section-title'
>Directories
</div>
260 <div class='section'
>
261 <script type='text/javascript'
>
262 createFieldTable('', [
263 { title
: 'Public Root Directory*', name
: 'ftp_pubroot', type
: 'text', maxlen
: 256, size
: 32,
264 suffix
: ' <small>(for authenticated users access)</small>',
265 value
: nvram
.ftp_pubroot
},
266 { title
: 'Private Root Directory**', name
: 'ftp_pvtroot', type
: 'text', maxlen
: 256, size
: 32,
267 suffix
: ' <small>(for authenticated users access in private mode)</small>',
268 value
: nvram
.ftp_pvtroot
},
269 { title
: 'Anonymous Root Directory*', name
: 'ftp_anonroot', type
: 'text', maxlen
: 256, size
: 32,
270 suffix
: ' <small>(for anonymous connections)</small>',
271 value
: nvram
.ftp_anonroot
},
272 { title
: 'Directory Listings', name
: 'ftp_dirlist', type
: 'select',
273 options
: [['0', 'Enabled'],['1', 'Disabled'],['2', 'Disabled for Anonymous']],
274 suffix
: ' <small>(always enabled for Admin)</small>',
275 value
: nvram
.ftp_dirlist
}
279 <br>*
When no directory is specified, /mnt is used as a root directory.
280 <br>**
In private mode, the root directory is the directory under the
"Private Root Directory" with the name matching the name of the user.
284 <div class='section-title'
>Limits
</div>
285 <div class='section'
>
286 <script type='text/javascript'
>
287 createFieldTable('', [
288 { title
: 'Maximum Users Allowed to Log in', name
: 'ftp_max', type
: 'text', maxlen
: 5, size
: 7,
289 suffix
: ' <small>(0 - unlimited)</small>',
290 value
: nvram
.ftp_max
},
291 { title
: 'Maximum Connections from the same IP', name
: 'ftp_ipmax', type
: 'text', maxlen
: 5, size
: 7,
292 suffix
: ' <small>(0 - unlimited)</small>',
293 value
: nvram
.ftp_ipmax
},
294 { title
: 'Maximum Bandwidth for Anonymous Users', name
: 'ftp_anonrate', type
: 'text', maxlen
: 5, size
: 7,
295 suffix
: ' <small>KBytes/sec (0 - unlimited)</small>',
296 value
: nvram
.ftp_anonrate
},
297 { title
: 'Maximum Bandwidth for Authenticated Users', name
: 'ftp_rate', type
: 'text', maxlen
: 5, size
: 7,
298 suffix
: ' <small>KBytes/sec (0 - unlimited)</small>',
299 value
: nvram
.ftp_rate
},
300 { title
: 'Idle Timeout', name
: 'ftp_staytimeout', type
: 'text', maxlen
: 5, size
: 7,
301 suffix
: ' <small>seconds (0 - no timeout)</small>',
302 value
: nvram
.ftp_staytimeout
},
303 { title
: 'Limit Connection Attempts', name
: 'f_limit', type
: 'checkbox',
304 value
: ftplimit
[0] != 0 },
305 { title
: '', indent
: 2, multi
: [
306 { name
: 'f_limit_hit', type
: 'text', maxlen
: 4, size
: 6, suffix
: ' <small>every</small> ', value
: ftplimit
[1] },
307 { name
: 'f_limit_sec', type
: 'text', maxlen
: 4, size
: 6, suffix
: ' <small>seconds</small>', value
: ftplimit
[2] }
313 <div class='section-title'
>Custom Configuration
</div>
314 <div class='section'
>
315 <script type='text/javascript'
>
316 createFieldTable('', [
317 { title
: '<a href="http://vsftpd.beasts.org/vsftpd_conf.html" target="_new">Vsftpd</a><br>Custom Configuration', name
: 'ftp_custom', type
: 'textarea', value
: nvram
.ftp_custom
}
322 <div class='section-title'
>User Accounts
</div>
323 <div class='section'
>
324 <table class='tomato-grid' cellspacing=
1 id='aft-grid'
></table>
325 <script type='text/javascript'
>aftg
.setup();</script>
331 <tr><td id='footer' colspan=
2>
332 <span id='footer-msg'
></span>
333 <input type='button' value='Save' id='save-button' onclick='save()'
>
334 <input type='button' value='Cancel' id='cancel-button' onclick='javascript:reloadPage();'
>
338 <script type='text/javascript'
>verifyFields(null, 1);</script>