1 /*
2 * part of owftpd By Paul H Alfille
3 * The whole is GPLv2 licenced though the ftp code was more liberally licenced when first used.
4 */
5
6 #include "owftpd.h"
7 #include <limits.h>
8 #include <stdarg.h>
9 #include <fnmatch.h>
10
11 /* AIX requires this to be the first thing in the file. */
12 #ifndef __GNUC__
13 # if HAVE_ALLOCA_H
14 # include <alloca.h>
15 # else
16 # ifdef _AIX
17 #pragma alloca
18 # else
19 # ifndef alloca /* predefined by HP cc +Olibcalls */
20 char *alloca();
21 # endif
22 # endif
23 # endif
24 #endif
25
26 static void WildLexCD(struct cd_parse_s *cps, ASCII * match);
27
FileLexCD(struct cd_parse_s * cps)28 void FileLexCD(struct cd_parse_s *cps)
29 {
30 struct parsedname pn;
31 while (1) {
32 switch (cps->pse) {
33 case parse_status_init:
34 LEVEL_DEBUG("FTP parse_status_init Path<%s> File <%s>", cps->buffer, cps->rest);
35 /* cps->buffer is absolute */
36 /* trailing / only at root */
37 cps->ret = 0;
38 cps->solutions = 0;
39 cps->dir = NULL;
40 if (cps->rest == NULL || cps->rest[0] == '\0') {
41 cps->pse = parse_status_tame;
42 } else {
43 if (cps->rest[0] == '/') { // root (absolute) specification
44 cps->buffer[1] = '\0';
45 ++cps->rest;
46 }
47 cps->pse = parse_status_init2;
48 }
49 break;
50 case parse_status_init2:
51 LEVEL_DEBUG("FTP parse_status_init2 Path<%s> File <%s>", cps->buffer, cps->rest);
52 /* cps->buffer is absolute */
53 /* trailing / only at root */
54 if ((cps->rest[0] == '.' && cps->rest[1] == '.')
55 || strpbrk(cps->rest, "*[?")) {
56 cps->pse = parse_status_back;
57 } else {
58 cps->pse = parse_status_tame;
59 }
60 break;
61 case parse_status_back:
62 LEVEL_DEBUG("FTP parse_status_back Path<%s> File <%s>", cps->buffer, cps->rest);
63 /* cps->buffer is absolute */
64 /* trailing / only at root */
65 if (cps->rest[0] == '.' && cps->rest[1] == '.') {
66 // Move back
67 ASCII *back = strrchr(cps->buffer, '/');
68 back[1] = '\0';
69 // look for next file part
70 if (cps->rest[2] == '\0') {
71 cps->pse = parse_status_last;
72 cps->rest = NULL;
73 } else if (cps->rest[2] == '/') {
74 cps->pse = parse_status_next;
75 cps->rest = &cps->rest[3];
76 } else {
77 cps->ret = -ENOENT;
78 return;
79 }
80 } else {
81 cps->pse = parse_status_next; // off the double dot trail
82 }
83 break;
84 case parse_status_next:
85 LEVEL_DEBUG("FTP parse_status_next Path<%s> File <%s>", cps->buffer, cps->rest);
86 /* cps->buffer is absolute */
87 /* trailing / only at root */
88 if (cps->rest == NULL || cps->rest[0] == '\0') {
89 cps->pse = parse_status_last;
90 } else {
91 ASCII *oldrest = strsep(&cps->rest, "/");
92 if (strpbrk(oldrest, "*[?")) {
93 WildLexCD(cps, oldrest);
94 return;
95 } else {
96 if (strlen(cps->buffer) + strlen(oldrest) + 4 > PATH_MAX) {
97 cps->ret = -ENAMETOOLONG;
98 return;
99 }
100 if (cps->buffer[1]) {
101 strcat(cps->buffer, "/");
102 }
103 strcat(cps->buffer, oldrest);
104 cps->pse = parse_status_next;
105 }
106 }
107 break;
108 case parse_status_tame:
109 LEVEL_DEBUG("FTP parse_status_tame Path<%s> File <%s>", cps->buffer, cps->rest);
110 /* cps->buffer is absolute */
111 /* trailing / only at root */
112 if (cps->rest && (strlen(cps->buffer) + strlen(cps->rest) + 4 > PATH_MAX)) {
113 cps->ret = -ENAMETOOLONG;
114 return;
115 }
116 if (cps->buffer[1]) {
117 strcat(cps->buffer, "/");
118 }
119 strcat(cps->buffer, cps->rest);
120 if (FS_ParsedName(cps->buffer, &pn) == 0) {
121 if (IsDir(&pn)) {
122 ++cps->solutions;
123 if (cps->solutions == 1) {
124 cps->dir = strdup(pn.path);
125 }
126 } else {
127 cps->ret = -ENOTDIR;
128 }
129 FS_ParsedName_destroy(&pn);
130 } else {
131 cps->ret = -ENOENT;
132 }
133 return;
134 case parse_status_last:
135 LEVEL_DEBUG("FTP parse_status_last Path<%s> File <%s>", cps->buffer, cps->rest);
136 /* cps->buffer is absolute */
137 /* trailing / only at root */
138 if (cps->rest && (strlen(cps->buffer) + strlen(cps->rest) + 4 > PATH_MAX)) {
139 cps->ret = -ENAMETOOLONG;
140 return;
141 }
142 if (FS_ParsedNamePlus(cps->buffer, cps->rest, &pn) == 0) {
143 if (IsDir(&pn)) {
144 ++cps->solutions;
145 if (cps->solutions == 1) {
146 cps->dir = strdup(pn.path);
147 }
148 } else {
149 cps->ret = -ENOTDIR;
150 }
151 FS_ParsedName_destroy(&pn);
152 }
153 return;
154 }
155 }
156 }
157
158 struct wildlexcd {
159 ASCII *end;
160 ASCII *match;
161 struct cd_parse_s *cps;
162 };
163
WildLexCDCallback(void * v,const struct parsedname * const pn_entry)164 static void WildLexCDCallback(void *v, const struct parsedname *const pn_entry)
165 {
166 struct wildlexcd *wlcd = v;
167 struct cd_parse_s cps;
168 strcpy(&wlcd->end[1], FS_DirName(pn_entry));
169 //printf("Try %s vs %s\n",end,match) ;
170 //if ( fnmatch( match, end, FNM_PATHNAME|FNM_CASEFOLD ) ) return ;
171 if (fnmatch(wlcd->match, &wlcd->end[1], FNM_PATHNAME)) {
172 return;
173 }
174 //printf("Match! %s\n",end) ;
175 memcpy(&cps, wlcd->cps, sizeof(cps));
176 cps.pse = parse_status_next;
177 FileLexCD(&cps);
178 }
179
WildLexCD(struct cd_parse_s * cps,ASCII * match)180 static void WildLexCD(struct cd_parse_s *cps, ASCII * match)
181 {
182 struct parsedname pn;
183
184 LEVEL_DEBUG("FTP Wildcard pattern matching: Path=%s, Pattern=%s, rest=%s", SAFESTRING(cps->buffer), SAFESTRING(match), SAFESTRING(cps->rest));
185 /* Check potential length */
186 if (strlen(cps->buffer) + OW_FULLNAME_MAX + 2 > PATH_MAX) {
187 cps->ret = -ENAMETOOLONG;
188 return;
189 }
190
191 if ( FS_ParsedName(cps->buffer, &pn) != 0 ) {
192 cps->ret = -ENOENT;
193 return;
194 }
195
196 if (!IsDir(&pn)) {
197 cps->ret = -ENOTDIR;
198 } else {
199 struct wildlexcd wlcd = { NULL, match, cps, };
200 int root = (cps->buffer[1] == '\0');
201
202 wlcd.end = &cps->buffer[strlen(cps->buffer)];
203 if (root) {
204 --wlcd.end;
205 }
206 wlcd.end[0] = '/';
207 FS_dir(WildLexCDCallback, &wlcd, &pn);
208 if (root) {
209 ++wlcd.end;
210 }
211 wlcd.end[0] = '\0'; // restore cps->buffer
212 }
213 FS_ParsedName_destroy(&pn);
214 }
215