Added a specific reply function for the data channel.
[uftps.git] / list_dir.c
blobe7004f8c2f62399be85b51b0369e3325a2ad7d9a
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 * LIST and NLST commands implementation. The boolean argument enables LIST
37 * mode, otherwise NSLT listing is sent.
39 * To make Mozilla Firefox list the directory contents correctly, some research
40 * work had to be done. That concluded in:
42 * http://cr.yp.to/ftp.html
44 * In particular:
46 * http://cr.yp.to/ftp/list/binls.html
48 * Only files or subdirectories are shown, the rest of items (symlinks, named
49 * pipes, sockets...) are ignored. The following line is sent when a file is
50 * found:
52 * -rw-r--r-- 1 ftp ftp 999 Mon 88 7777 filename
54 * In case of a subdirectory, the line is:
56 * drwxr-xr-x 1 ftp ftp 999 Mon 88 7777 dirname
58 * Where '999' is the item size, 'Mon 88 7777' is the month, day and year,
59 * respectively, of the last modification date. Some servers display the hour
60 * and minute when the distance between current time and last modification time
61 * is less than six months. We don't do that as it is considered irrelevant.
62 * To obtain a more precise value for the last modification time, let the client
63 * send an MDTM command.
65 * Note that most clients will appreciate listings where all its items are
66 * stat()-able.
68 void list_dir (int full_list)
70 int len, l, e;
71 DIR *dir;
72 struct dirent *dentry;
73 struct stat s;
74 struct tm t;
75 char item[512];
77 /* Workaround for Konqueror and Nautilus */
78 if (SS.arg != NULL && SS.arg[0] == '-')
79 SS.arg = NULL;
80 len = expand_arg();
82 dir = opendir(SS.arg);
83 if (dir == NULL)
85 error("Opening directory '%s'", SS.arg);
86 reply_c("550 Could not open directory.\r\n");
87 return;
90 /* Prepare the argument for the listing, see below */
91 if (len > 3)
93 SS.arg[len - 1] = '/';
94 len++;
97 e = open_data_channel();
98 if (e == -1)
100 closedir(dir);
101 return;
104 reply_c("150 Sending directory list.\r\n");
106 do {
107 dentry = readdir(dir);
108 if (dentry == NULL)
109 break;
112 * Due to the chroot emulation, each dentry needs to be
113 * prepended by the expanded argument (stored in the auxiliary
114 * buffer) after checking bounds. Otherwise, the stat() call
115 * would fail.
117 l = strlen(dentry->d_name);
118 if (len + l >= LINE_SIZE)
119 fatal("Path overflow in LIST/NLST");
120 strcpy(&SS.arg[len - 1], dentry->d_name);
122 debug("Stating %s", SS.arg);
123 e = lstat(SS.arg, &s);
124 if (e == -1 || (!S_ISDIR(s.st_mode) && !S_ISREG(s.st_mode)))
126 debug("Dentry %s skipped", SS.arg);
127 continue;
130 if (full_list)
132 /* LIST */
133 gmtime_r(&(s.st_mtime), &t);
134 l = snprintf(item, 512,
135 "%s 1 ftp ftp %13lld %s %3d %4d %s\r\n",
136 (S_ISDIR(s.st_mode) ? "dr-xr-xr-x"
137 : "-r--r--r--"), (long long) s.st_size,
138 month[t.tm_mon], t.tm_mday, t.tm_year + 1900,
139 dentry->d_name);
141 else
143 /* NLST */
144 l = snprintf(item, 512, "%s%s", dentry->d_name,
145 (S_ISDIR(s.st_mode) ? "/\r\n" : "\r\n"));
148 e = data_reply(item, l);
149 } while (e == 0);
151 if (e == 0)
152 reply_c("226 Directory list sent.\r\n");
153 else
154 reply_c("426 Connection closed, transfer aborted.\r\n");
156 closedir(dir);
157 close(SS.data_sk);
158 SS.data_sk = -1;