Renamed session structure.
[uftps.git] / list_dir.c
blob87499a7ec615b8e20eeeba547d9255fd6bc842c0
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
19 #include "uftps.h"
22 * LIST and NLST commands implementation.
24 * To make Mozilla Firefox list the directory contents correctly, some research
25 * work had to be done. That concluded in:
27 * http://cr.yp.to/ftp.html
29 * In particular:
31 * http://cr.yp.to/ftp/list/binls.html
33 * Only files or subdirectories are shown, the rest of items (symlinks, named
34 * pipes, sockets...) are ignored. The following line is sent when a file is
35 * found:
37 * -rw-r--r-- 1 ftp ftp 999 Mon 88 7777 filename
39 * In case of a subdirectory, the line is:
41 * drwxr-xr-x 1 ftp ftp 999 Mon 88 7777 dirname
43 * Where '999' is the item size, 'Mon 88 7777' is the month, day and year,
44 * respectively, of the last modification date. Some servers display the hour
45 * and minute when the distance between current time and last modification time
46 * is less than six months. We don't do that as it is considered irrelevant.
47 * To obtain a more precise value for the last modification time, let the client
48 * send a MDTM command.
50 * Note that when, for some reason, directory listing is not possible, an empty
51 * list is sent. So this error is not detected from the client, until it tries
52 * to execute CWD.
54 * Also note that most clients will appreciate listings where all its items are
55 * stat()-able.
58 #include <sys/types.h>
59 #include <sys/stat.h>
60 #include <sys/socket.h>
61 #include <netinet/in.h>
62 #include <unistd.h>
63 #include <dirent.h>
64 #include <time.h>
65 #include <string.h>
66 #include <stdio.h>
69 /* Month names */
70 static char month[12][4] = {
71 "Jan\0", "Feb\0", "Mar\0", "Apr\0", "May\0", "Jun\0", "Jul\0", "Aug\0",
72 "Sep\0", "Oct\0", "Nov\0", "Dec\0"
77 * Temporary workaround
79 static void send_data (int sk, const char *str, int len)
81 int b;
83 do {
84 b = write(sk, str, len);
85 if (b <= 0)
86 return;
88 str += b;
89 len -= b;
90 } while (len > 0);
94 void list_dir (int full_list)
96 int len, l, err;
97 DIR *dir;
98 struct dirent *dentry;
99 struct sockaddr_in saddr;
100 struct stat st;
101 struct tm t;
102 socklen_t saddr_len = sizeof(saddr);
103 char item[512];
105 if (SS.passive_mode)
106 SS.data_sk = accept(SS.passive_bind_sk,
107 (struct sockaddr *) &saddr, &saddr_len);
109 /* Workaround for Konqueror and Nautilus */
110 if (SS.arg != NULL && SS.arg[0] == '-')
111 SS.arg = NULL;
113 len = expand_arg();
114 dir = opendir(SS.arg);
115 if (len > 3)
117 SS.arg[len - 1] = '/';
118 len++;
121 reply_c("150 Sending directory list.\r\n");
122 if (dir == NULL)
123 goto finish;
125 do {
126 dentry = readdir(dir);
127 if (dentry == NULL)
128 break;
130 strcpy(&SS.arg[len - 1], dentry->d_name);
131 debug("Stating '%s'", SS.arg);
132 err = stat(SS.arg, &st);
133 if (err == -1)
134 continue;
136 if (full_list)
138 /* LIST */
139 gmtime_r(&(st.st_mtime), &t);
140 l = snprintf(item, 512,
141 "%s 1 ftp ftp %13lld %s %3d %4d %s\r\n",
142 (S_ISDIR(st.st_mode) ? "dr-xr-xr-x"
143 : "-r--r--r--"), (long long) st.st_size,
144 month[t.tm_mon], t.tm_mday, t.tm_year + 1900,
145 dentry->d_name);
147 else
149 /* NLST */
150 l = snprintf(item, 512, "%s%s", dentry->d_name,
151 (dentry->d_type == DT_DIR ? "/\r\n"
152 : "\r\n"));
155 send_data(SS.data_sk, item, l);
157 } while (1);
158 closedir(dir);
160 finish:
161 reply_c("226 Directory list sent.\r\n");
162 close(SS.data_sk);
163 SS.data_sk = -1;
164 SS.passive_mode = 0;