1 /*
2 * ProFTPD - mod_sftp Display files
3 * Copyright (c) 2010-2020 TJ Saunders
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 *
19 * As a special exemption, TJ Saunders and other respective copyright holders
20 * give permission to link this program with OpenSSL, and distribute the
21 * resulting executable, without including the source code for OpenSSL in the
22 * source distribution.
23 */
24
25 /* Display of files */
26
27 #include "mod_sftp.h"
28 #include "display.h"
29 #include "packet.h"
30 #include "msg.h"
31
32 /* Note: The size provided by pr_fs_getsize2() is in KB, not bytes. */
format_size_str(char * buf,size_t buflen,off_t size)33 static void format_size_str(char *buf, size_t buflen, off_t size) {
34 char *units[] = {"K", "M", "G", "T", "P", "E", "Z", "Y"};
35 unsigned int nunits = 8;
36 register unsigned int i = 0;
37 int res;
38
39 /* Determine the appropriate units label to use. Do not exceed the max
40 * possible unit support (yottabytes), by ensuring that i maxes out at
41 * index 7 (of 8 possible units).
42 */
43 while (size > 1024 &&
44 i < (nunits - 1)) {
45 pr_signals_handle();
46
47 size /= 1024;
48 i++;
49 }
50
51 /* Now, prepare the buffer. */
52 res = pr_snprintf(buf, buflen, "%.3" PR_LU "%sB", (pr_off_t) size, units[i]);
53 if (res > 2) {
54 /* Check for leading zeroes; it's an aethetic choice. */
55 if (buf[0] == '0' && buf[1] != '.') {
56 memmove(&buf[0], &buf[1], res-1);
57 buf[res-1] = '\0';
58 }
59 }
60 }
61
sftp_display_fh_get_msg(pool * p,pr_fh_t * fh)62 const char *sftp_display_fh_get_msg(pool *p, pr_fh_t *fh) {
63 struct stat st;
64 char buf[PR_TUNABLE_BUFFER_SIZE], *msg = "";
65 int len, res;
66 const unsigned int *current_clients = NULL;
67 const unsigned int *max_clients = NULL;
68 off_t fs_size = 0;
69 const void *v;
70 const char *outs, *rfc1413_ident, *user;
71 const char *serverfqdn = main_server->ServerFQDN;
72 char mg_size[12] = {'\0'}, mg_size_units[12] = {'\0'},
73 mg_max[12] = "unlimited";
74 char mg_class_limit[12] = {'\0'}, mg_cur[12] = {'\0'},
75 mg_cur_class[12] = {'\0'};
76 const char *mg_time;
77
78 /* Stat the opened file to determine the optimal buffer size for IO. */
79 memset(&st, 0, sizeof(st));
80 if (pr_fsio_fstat(fh, &st) == 0) {
81 fh->fh_iosz = st.st_blksize;
82 }
83
84 res = pr_fs_fgetsize(fh->fh_fd, &fs_size);
85 if (res < 0 &&
86 errno != ENOSYS) {
87 (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
88 "error getting filesystem size for '%s': %s", fh->fh_path,
89 strerror(errno));
90 fs_size = 0;
91 }
92
93 pr_snprintf(mg_size, sizeof(mg_size), "%" PR_LU, (pr_off_t) fs_size);
94 format_size_str(mg_size_units, sizeof(mg_size_units), fs_size);
95
96 mg_time = pr_strtime3(p, time(NULL), FALSE);
97
98 max_clients = get_param_ptr(main_server->conf, "MaxClients", FALSE);
99
100 v = pr_table_get(session.notes, "client-count", NULL);
101 if (v != NULL) {
102 current_clients = v;
103 }
104
105 pr_snprintf(mg_cur, sizeof(mg_cur), "%u",
106 current_clients ? *current_clients: 1);
107
108 if (session.conn_class != NULL &&
109 session.conn_class->cls_name) {
110 const unsigned int *class_clients = NULL;
111 config_rec *maxc = NULL;
112 unsigned int maxclients = 0;
113
114 v = pr_table_get(session.notes, "class-client-count", NULL);
115 if (v != NULL) {
116 class_clients = v;
117 }
118
119 pr_snprintf(mg_cur_class, sizeof(mg_cur_class), "%u",
120 class_clients ? *class_clients : 0);
121
122 /* For the %z variable, first we scan through the MaxClientsPerClass,
123 * and use the first applicable one. If none are found, look for
124 * any MaxClients set.
125 */
126
127 maxc = find_config(main_server->conf, CONF_PARAM, "MaxClientsPerClass",
128 FALSE);
129 while (maxc != NULL) {
130 pr_signals_handle();
131
132 if (strcmp(maxc->argv[0], session.conn_class->cls_name) != 0) {
133 maxc = find_config_next(maxc, maxc->next, CONF_PARAM,
134 "MaxClientsPerClass", FALSE);
135 continue;
136 }
137
138 maxclients = *((unsigned int *) maxc->argv[1]);
139 break;
140 }
141
142 if (maxclients == 0) {
143 maxc = find_config(main_server->conf, CONF_PARAM, "MaxClients", FALSE);
144 if (maxc) {
145 maxclients = *((unsigned int *) maxc->argv[0]);
146 }
147 }
148
149 pr_snprintf(mg_class_limit, sizeof(mg_class_limit), "%u", maxclients);
150
151 } else {
152 pr_snprintf(mg_class_limit, sizeof(mg_class_limit), "%u",
153 max_clients ? *max_clients : 0);
154 pr_snprintf(mg_cur_class, sizeof(mg_cur_class), "%u", 0);
155 }
156
157 pr_snprintf(mg_max, sizeof(mg_max), "%u", max_clients ? *max_clients : 0);
158
159 user = pr_table_get(session.notes, "mod_auth.orig-user", NULL);
160 if (user == NULL) {
161 user = "";
162 }
163
164 rfc1413_ident = pr_table_get(session.notes, "mod_ident.rfc1413-ident", NULL);
165 if (rfc1413_ident == NULL) {
166 rfc1413_ident = "UNKNOWN";
167 }
168
169 memset(buf, '\0', sizeof(buf));
170 while (pr_fsio_gets(buf, sizeof(buf), fh) != NULL) {
171 char *tmp;
172
173 pr_signals_handle();
174
175 buf[sizeof(buf)-1] = '\0';
176 len = strlen(buf);
177
178 while (len > 0 &&
179 (buf[len-1] == '\r' || buf[len-1] == '\n')) {
180 pr_signals_handle();
181
182 buf[len-1] = '\0';
183 len--;
184 }
185
186 /* Check for any Variable-type strings. */
187 tmp = strstr(buf, "%{");
188 while (tmp) {
189 char *key, *tmp2;
190 const char *val;
191
192 pr_signals_handle();
193
194 tmp2 = strchr(tmp, '}');
195 if (tmp2 == NULL) {
196 /* No closing '}' found in this string, so no need to look for any
197 * another '%{' opening sequence. Just move on.
198 */
199 tmp = NULL;
200 break;
201 }
202
203 key = pstrndup(p, tmp, tmp2 - tmp + 1);
204
205 /* There are a couple of special-case keys to watch for:
206 *
207 * env:$var
208 * time:$fmt
209 *
210 * The Var API does not easily support returning values for keys
211 * where part of the value depends on part of the key. That's why
212 * these keys are handled here, instead of in pr_var_get().
213 */
214
215 if (strncmp(key, "%{time:", 7) == 0) {
216 char time_str[128], *fmt;
217 time_t now;
218 struct tm *tm;
219
220 fmt = pstrndup(p, key + 7, strlen(key) - 8);
221
222 now = time(NULL);
223 memset(time_str, 0, sizeof(time_str));
224
225 tm = pr_localtime(p, &now);
226 if (tm != NULL) {
227 strftime(time_str, sizeof(time_str), fmt, tm);
228 }
229
230 val = pstrdup(p, time_str);
231
232 } else if (strncmp(key, "%{env:", 6) == 0) {
233 char *env_var;
234
235 env_var = pstrndup(p, key + 6, strlen(key) - 7);
236 val = pr_env_get(p, env_var);
237 if (val == NULL) {
238 pr_trace_msg("var", 4,
239 "no value set for environment variable '%s', using \"(none)\"",
240 env_var);
241 val = "(none)";
242 }
243
244 } else {
245 val = pr_var_get(key);
246 if (val == NULL) {
247 pr_trace_msg("var", 4,
248 "no value set for name '%s', using \"(none)\"", key);
249 val = "(none)";
250 }
251 }
252
253 outs = sreplace(p, buf, key, val, NULL);
254 sstrncpy(buf, outs, sizeof(buf));
255
256 tmp = strstr(outs, "%{");
257 }
258
259 outs = sreplace(p, buf,
260 "%C", (session.cwd[0] ? session.cwd : "(none)"),
261 "%E", main_server->ServerAdmin,
262 "%F", mg_size,
263 "%f", mg_size_units,
264 "%i", "0",
265 "%K", "0",
266 "%k", "0B",
267 "%L", serverfqdn,
268 "%M", mg_max,
269 "%N", mg_cur,
270 "%o", "0",
271 "%R", (session.c && session.c->remote_name ?
272 session.c->remote_name : "(unknown)"),
273 "%T", mg_time,
274 "%t", "0",
275 "%U", user,
276 "%u", rfc1413_ident,
277 "%V", main_server->ServerName,
278 "%x", session.conn_class ? session.conn_class->cls_name : "(unknown)",
279 "%y", mg_cur_class,
280 "%z", mg_class_limit,
281 NULL);
282
283 /* Always make sure that the lines we send are CRLF-terminated. */
284 msg = pstrcat(p, msg, outs, "\r\n", NULL);
285
286 /* Clear the buffer for the next read. */
287 memset(buf, '\0', sizeof(buf));
288 }
289
290 return msg;
291 }
292