Removed error checks for close() call.
[uftps.git] / list_dir.c
blobf59e23db17a1264509b92978af1242e02c3b77c6
1 /*
2 * User FTP Server, Share folders over FTP without being root.
3 * Copyright (C) 2008 Isaac Jurado
5 * This program is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU General Public License as published by the Free Software
7 * Foundation; either version 2 of the License, or (at your option) any later
8 * version.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13 * details.
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
20 #include "uftps.h"
21 #include <sys/stat.h>
22 #include <unistd.h>
23 #include <dirent.h>
24 #include <time.h>
25 #include <string.h>
26 #include <stdio.h>
28 /* Month names */
29 static const char const month[12][4] = {
30 "Jan\0", "Feb\0", "Mar\0", "Apr\0", "May\0", "Jun\0", "Jul\0", "Aug\0",
31 "Sep\0", "Oct\0", "Nov\0", "Dec\0"
36 * Send a listing line of length "len", contained in "str" over the socket "sk".
37 * Return 0 when all "len" bytes has been transferred or -1 if there was an
38 * error or less bytes were sent.
40 static int send_data_line (int sk, const char *str, int len)
42 int b, l = 0;
44 while (l < len)
46 b = send(sk, &str[l], len - l, 0);
47 if (b <= 0)
49 error("Sending listing data");
50 return -1;
53 l += b;
56 return 0;
61 * LIST and NLST commands implementation. The boolean argument enables LIST
62 * mode, otherwise NSLT listing is sent.
64 * To make Mozilla Firefox list the directory contents correctly, some research
65 * work had to be done. That concluded in:
67 * http://cr.yp.to/ftp.html
69 * In particular:
71 * http://cr.yp.to/ftp/list/binls.html
73 * Only files or subdirectories are shown, the rest of items (symlinks, named
74 * pipes, sockets...) are ignored. The following line is sent when a file is
75 * found:
77 * -rw-r--r-- 1 ftp ftp 999 Mon 88 7777 filename
79 * In case of a subdirectory, the line is:
81 * drwxr-xr-x 1 ftp ftp 999 Mon 88 7777 dirname
83 * Where '999' is the item size, 'Mon 88 7777' is the month, day and year,
84 * respectively, of the last modification date. Some servers display the hour
85 * and minute when the distance between current time and last modification time
86 * is less than six months. We don't do that as it is considered irrelevant.
87 * To obtain a more precise value for the last modification time, let the client
88 * send an MDTM command.
90 * Note that most clients will appreciate listings where all its items are
91 * stat()-able.
93 void list_dir (int full_list)
95 int len, l, e;
96 DIR *dir;
97 struct dirent *dentry;
98 struct stat s;
99 struct tm t;
100 char item[512];
102 /* Workaround for Konqueror and Nautilus */
103 if (SS.arg != NULL && SS.arg[0] == '-')
104 SS.arg = NULL;
105 len = expand_arg();
107 dir = opendir(SS.arg);
108 if (dir == NULL)
110 error("Opening directory '%s'", SS.arg);
111 reply_c("550 Could not open directory.\r\n");
112 return;
115 /* Prepare the argument for the listing, see below */
116 if (len > 3)
118 SS.arg[len - 1] = '/';
119 len++;
122 e = open_data_channel();
123 if (e == -1)
125 closedir(dir);
126 return;
129 reply_c("150 Sending directory list.\r\n");
131 do {
132 dentry = readdir(dir);
133 if (dentry == NULL)
134 break;
137 * Due to the chroot emulation, each dentry needs to be
138 * prepended by the expanded argument (stored in the auxiliary
139 * buffer) after checking bounds. Otherwise, the stat() call
140 * would fail.
142 l = strlen(dentry->d_name);
143 if (len + l >= LINE_SIZE)
144 fatal("Path overflow in LIST/NLST");
145 strcpy(&SS.arg[len - 1], dentry->d_name);
147 debug("Stating %s", SS.arg);
148 e = lstat(SS.arg, &s);
149 if (e == -1 || (!S_ISDIR(s.st_mode) && !S_ISREG(s.st_mode)))
151 debug("Dentry %s skipped", SS.arg);
152 continue;
155 if (full_list)
157 /* LIST */
158 gmtime_r(&(s.st_mtime), &t);
159 l = snprintf(item, 512,
160 "%s 1 ftp ftp %13lld %s %3d %4d %s\r\n",
161 (S_ISDIR(s.st_mode) ? "dr-xr-xr-x"
162 : "-r--r--r--"), (long long) s.st_size,
163 month[t.tm_mon], t.tm_mday, t.tm_year + 1900,
164 dentry->d_name);
166 else
168 /* NLST */
169 l = snprintf(item, 512, "%s%s", dentry->d_name,
170 (S_ISDIR(s.st_mode) ? "/\r\n" : "\r\n"));
173 e = send_data_line(SS.data_sk, item, l);
174 } while (e == 0);
176 if (e == 0)
177 reply_c("226 Directory list sent.\r\n");
178 else
179 reply_c("426 Connection closed, transfer aborted.\r\n");
181 closedir(dir);
182 close(SS.data_sk);
183 SS.data_sk = -1;