Dillo v3.1.1-98-g318d1f14
Loading...
Searching...
No Matches
ftp.c
Go to the documentation of this file.
1/*
2 * Dpi for FTP.
3 *
4 * This server checks the ftp-URL to be a directory (requires wget).
5 * If true, it sends back an html representation of it, and if not
6 * a dpip message (which is caught by dillo who redirects the ftp URL
7 * to the downloads server).
8 *
9 * Feel free to polish!
10 *
11 * Copyright 2003-2007 Jorge Arellano Cid <jcid@dillo.org>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 3 of the License, or
16 * (at your option) any later version.
17 *
18 */
19
20/*
21 * TODO:
22 * - Send feedback about the FTP login process from wget's stderr.
23 * i.e. capture our child's stderr, process it, and report back.
24 * - Handle simultaneous connections.
25 * If ftp.dpi is implemented with a low level ftp library, it becomes
26 * possible to keep the connection alive, and thus make browsing of ftp
27 * directories faster (this avoids one login per page, and forks). Perhaps
28 * it's not worth, but can be done.
29 */
30
31#include <unistd.h>
32#include <sys/types.h>
33#include <sys/socket.h>
34#include <sys/un.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <signal.h>
39#include <sys/wait.h>
40#include <errno.h>
41#include <sys/time.h>
42#include <ctype.h>
43
44#include "../dpip/dpip.h"
45#include "dpiutil.h"
46#include "d_size.h"
47
48/*
49 * Debugging macros
50 * (Set debugging messages to stderr, to see them)
51 */
52#define _MSG(...)
53//#define MSG(...) fprintf(stderr, "[ftp dpi]: " __VA_ARGS__)
54#define MSG(...) printf("[ftp dpi]: " __VA_ARGS__)
55
56/*
57 * Global variables
58 */
59static Dsh *sh = NULL;
60static char **dl_argv = NULL;
61
62/*---------------------------------------------------------------------------*/
63
64/* TODO: could use dStr ADT! */
65typedef struct {
66 const char *str;
67 int len;
68} ContentType_t;
69
70static const ContentType_t MimeTypes[] = {
71 { "application/octet-stream", 24 },
72 { "text/html", 9 },
73 { "text/plain", 10 },
74 { "image/gif", 9 },
75 { "image/png", 9 },
76 { "image/jpeg", 10 },
77 { NULL, 0 }
78};
79
80/*
81 * Detects 'Content-Type' from a data stream sample.
82 *
83 * It uses the magic(5) logic from file(1). Currently, it
84 * only checks the few mime types that Dillo supports.
85 *
86 * 'Data' is a pointer to the first bytes of the raw data.
87 *
88 * Return value: (0 on success, 1 on doubt, 2 on lack of data).
89 */
90static int a_Misc_get_content_type_from_data2(void *Data, size_t Size,
91 const char **PT)
92{
93 int st = 1; /* default to "doubt' */
94 int Type = 0; /* default to "application/octet-stream" */
95 char *p = Data;
96 uchar_t ch;
97 size_t i, non_ascci;
98
99 /* HTML try */
100 for (i = 0; i < Size && dIsspace(p[i]); ++i);
101 if ((Size - i >= 5 && !dStrnAsciiCasecmp(p+i, "<html", 5)) ||
102 (Size - i >= 5 && !dStrnAsciiCasecmp(p+i, "<head", 5)) ||
103 (Size - i >= 6 && !dStrnAsciiCasecmp(p+i, "<title", 6)) ||
104 (Size - i >= 14 && !dStrnAsciiCasecmp(p+i, "<!doctype html", 14)) ||
105 /* this line is workaround for FTP through the Squid proxy */
106 (Size - i >= 17 && !dStrnAsciiCasecmp(p+i, "<!-- HTML listing", 17))) {
107
108 Type = 1;
109 st = 0;
110 /* Images */
111 } else if (Size >= 4 && !strncmp(p, "GIF8", 4)) {
112 Type = 3;
113 st = 0;
114 } else if (Size >= 4 && !strncmp(p, "\x89PNG", 4)) {
115 Type = 4;
116 st = 0;
117 } else if (Size >= 2 && !strncmp(p, "\xff\xd8", 2)) {
118 /* JPEG has the first 2 bytes set to 0xffd8 in BigEndian - looking
119 * at the character representation should be machine independent. */
120 Type = 5;
121 st = 0;
122
123 /* Text */
124 } else {
125 /* We'll assume "text/plain" if the set of chars above 127 is <= 10%
126 * of the sample. This helps to catch ASCII, LATIN1 and UTF-8 as text.
127 * Better heuristics are welcomed! :-) */
128 non_ascci = 0;
129 Size = MIN (Size, 256);
130 for (i = 0; i < Size; i++) {
131 ch = (uchar_t) p[i];
132 if ((ch < 32 || ch > 126) && !dIsspace(ch))
133 ++non_ascci;
134 }
135 if (Size == 256) {
136 Type = (non_ascci > Size/10) ? 0 : 2;
137 st = 0;
138 } else {
139 Type = (non_ascci > Size/10) ? 0 : 2;
140 }
141 }
142
143 *PT = MimeTypes[Type].str;
144 return st;
145}
146
147/*---------------------------------------------------------------------------*/
148
149/*
150 * Build a shell command using wget for this URL.
151 */
152static void make_wget_argv(char *url)
153{
154 char *esc_url;
155
156 if (dl_argv) {
157 dFree(dl_argv[3]);
158 dFree(dl_argv);
159 }
160 dl_argv = dNew(char*, 10);
161
162 esc_url = Escape_uri_str(url, "'");
163 /* avoid malicious SMTP relaying with FTP urls */
164 Filter_smtp_hack(esc_url);
165
166 dl_argv[0] = "wget";
167 dl_argv[1] = "-t1"; /* try once, default is 20 */
168 dl_argv[2] = "-O-";
169 dl_argv[3] = esc_url;
170 dl_argv[4] = NULL;
171}
172
173/*
174 * Fork, exec command, get its output and send via stdout.
175 * Return: Number of bytes transfered, -1 if file-not_found, -2 if aborted.
176 */
177static int try_ftp_transfer(char *url)
178{
179#define MIN_SZ 256
180#define READ_SZ 16*1024
181
182 ssize_t n;
183 int nb, has_mime_type, has_html_header, no_such_file, offer_download;
184 const char *mime_type = "application/octet-stream";
185 char buf[READ_SZ], *d_cmd;
186 Dstr *dbuf = dStr_sized_new(READ_SZ);
187 pid_t ch_pid;
188 int aborted = 0;
189 int DataPipe[2];
190
191 MSG("try_ftp_transfer: url=%s\n", url);
192
193 if (pipe(DataPipe) < 0) {
194 MSG("pipe, %s\n", dStrerror(errno));
195 return 0;
196 }
197
198 /* Prepare args for execvp() */
199 make_wget_argv(url);
200
201 /* Start the child process */
202 if ((ch_pid = fork()) == 0) {
203 /* child */
204 /* start wget */
205 close(DataPipe[0]);
206 dup2(DataPipe[1], 1); /* stdout */
207 execvp(dl_argv[0], dl_argv);
208 _exit(1);
209 } else if (ch_pid < 0) {
210 perror("fork, ");
211 exit(1);
212 } else {
213 /* father continues below */
214 close(DataPipe[1]);
215 }
216
217 /* Read/Write the real data */
218 nb = 0;
219 has_mime_type = 0;
220 has_html_header = 0;
221 no_such_file = 0;
222 offer_download = 0;
223 do {
224 while ((n = read(DataPipe[0], buf, READ_SZ)) < 0 && errno == EINTR);
225 if (n > 0) {
226 dStr_append_l(dbuf, buf, n);
227 if (!has_mime_type && dbuf->len < MIN_SZ)
228 continue;
229 } else if (n < 0)
230 break;
231
232 if (!has_mime_type) {
233 if (dbuf->len == 0) {
234 /* When the file doesn't exist, the transfer size is zero */
235 no_such_file = 1;
236 break;
237 }
238 a_Misc_get_content_type_from_data2(dbuf->str, dbuf->len, &mime_type);
239 has_mime_type = 1;
240
241 if (strcmp(mime_type, "application/octet-stream") == 0) {
242 /* abort transfer */
243 kill(ch_pid, SIGTERM);
244 /* The "application/octet-stream" MIME type will be sent and
245 * Dillo will offer a download dialog */
246 offer_download = 1;
247 aborted = 1;
248 }
249 }
250
251 if (offer_download || (!aborted && !has_html_header && dbuf->len)) {
252 /* Send dpip tag */
253 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
254 a_Dpip_dsh_write_str(sh, 1, d_cmd);
255 dFree(d_cmd);
256
257 /* Send HTTP header. */
258 a_Dpip_dsh_write_str(sh, 0, "Content-type: ");
259 a_Dpip_dsh_write_str(sh, 0, mime_type);
260 a_Dpip_dsh_write_str(sh, 1, "\r\n\r\n");
261 has_html_header = 1;
262 }
263
264 if (!aborted && dbuf->len) {
265 a_Dpip_dsh_write(sh, 1, dbuf->str, dbuf->len);
266 nb += dbuf->len;
267 dStr_truncate(dbuf, 0);
268 }
269 } while (n > 0 && !aborted);
270
271 dStr_free(dbuf, 1);
272 return (no_such_file ? -1 : (aborted ? -2 : nb));
273}
274
275/*
276 *
277 */
278int main(int argc, char **argv)
279{
280 const char *err_msg = "404 Not Found\nNo such file or directory";
281 char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *url2 = NULL;
282 int st, rc;
283 char *p, *d_cmd;
284
285 /* wget may need to write a temporary file... */
286 rc = chdir("/tmp");
287 if (rc == -1) {
288 MSG("paths: error changing directory to /tmp: %s\n",
289 dStrerror(errno));
290 }
291
292 /* Initialize the SockHandler */
293 sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
294
295 if (argc == 2) {
296 /* Debugging with a command line argument */
297 dpip_tag = dStrdup(argv[1]);
298 } else {
299 /* Authenticate our client... */
300 if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
301 a_Dpip_check_auth(dpip_tag) < 0) {
302 MSG("can't authenticate request: %s\n", dStrerror(errno));
304 return 1;
305 }
306 dFree(dpip_tag);
307 /* Read the dpi command from STDIN */
308 dpip_tag = a_Dpip_dsh_read_token(sh, 1);
309 }
310 MSG("tag=[%s]\n", dpip_tag);
311
312 cmd = a_Dpip_get_attr(dpip_tag, "cmd");
313 url = a_Dpip_get_attr(dpip_tag, "url");
314 if (!cmd || !url) {
315 MSG("ERROR, cmd=%s, url=%s\n", cmd, url);
316 exit (EXIT_FAILURE);
317 }
318
319 if ((st = try_ftp_transfer(url)) == -1) {
320 /* Transfer failed, the requested file may not exist or be a symlink
321 * to a directory. Try again... */
322 if ((p = strrchr(url, '/')) && p[1] &&
323 p > url && p[-1] != '/') {
324 url2 = dStrconcat(url, "/", NULL);
325 st = try_ftp_transfer(url2);
326 }
327 }
328
329 if (st == -1) {
330 /* The transfer failed, let dillo know... */
331 d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
332 a_Dpip_dsh_write_str(sh, 0, d_cmd);
333 dFree(d_cmd);
335 "HTTP/1.1 404 Not Found\r\n"
336 "Content-Type: text/plain\r\n"
337 "Content-Length: %d\r\n"
338 "\r\n"
339 "%s",
340 strlen(err_msg), err_msg);
341 }
342
343 dFree(cmd);
344 dFree(url);
345 dFree(url2);
346 dFree(dpip_tag);
347
348 /* Finish the SockHandler */
351
352 return 0;
353}
354
int main(void)
Definition bookmarks.c:1613
unsigned char uchar_t
Definition d_size.h:17
char * dStrconcat(const char *s1,...)
Concatenate a NULL-terminated list of strings.
Definition dlib.c:102
void dFree(void *mem)
Definition dlib.c:68
char * dStrdup(const char *s)
Definition dlib.c:77
Dstr * dStr_sized_new(int sz)
Create a new string with a given size.
Definition dlib.c:254
int dStrnAsciiCasecmp(const char *s1, const char *s2, size_t n)
Definition dlib.c:215
void dStr_free(Dstr *ds, int all)
Free a dillo string.
Definition dlib.c:337
void dStr_append_l(Dstr *ds, const char *s, int l)
Append a C string to a Dstr (providing length).
Definition dlib.c:308
void dStr_truncate(Dstr *ds, int len)
Truncate a Dstr to be 'len' bytes long.
Definition dlib.c:368
#define dStrerror
Definition dlib.h:95
#define MIN(a, b)
Definition dlib.h:30
#define dIsspace(c)
Definition dlib.h:33
#define dNew(type, count)
Definition dlib.h:49
void a_Dpip_dsh_free(Dsh *dsh)
Free the SockHandler structure.
Definition dpip.c:525
char * a_Dpip_build_cmd(const char *format,...)
Printf like function for building dpip commands.
Definition dpip.c:83
int a_Dpip_dsh_write_str(Dsh *dsh, int flush, const char *str)
Convenience function.
Definition dpip.c:374
char * a_Dpip_get_attr(const char *tag, const char *attrname)
Task: given a tag and an attribute name, return its value.
Definition dpip.c:192
char * a_Dpip_dsh_read_token(Dsh *dsh, int blocking)
Return a newlly allocated string with the next dpip token in the socket.
Definition dpip.c:493
void a_Dpip_dsh_close(Dsh *dsh)
Close this socket for reading and writing.
Definition dpip.c:504
int a_Dpip_check_auth(const char *auth_tag)
Check whether the given 'auth' string equals what dpid saved.
Definition dpip.c:201
int a_Dpip_dsh_write(Dsh *dsh, int flush, const char *Data, int DataSize)
Streamed write to socket.
Definition dpip.c:317
Dsh * a_Dpip_dsh_new(int fd_in, int fd_out, int flush_sz)
Create and initialize a dpip socket handler.
Definition dpip.c:247
#define a_Dpip_dsh_printf(sh, flush,...)
Definition dpip.h:77
char * Filter_smtp_hack(char *url)
Definition dpiutil.c:148
char * Escape_uri_str(const char *str, const char *p_esc_set)
Definition dpiutil.c:36
#define READ_SZ
static void make_wget_argv(char *url)
Definition ftp.c:152
static Dsh * sh
Definition ftp.c:59
#define MSG(...)
Definition ftp.c:54
static int try_ftp_transfer(char *url)
Definition ftp.c:177
static const ContentType_t MimeTypes[]
Definition ftp.c:70
#define MIN_SZ
static char ** dl_argv
Definition ftp.c:60
static int a_Misc_get_content_type_from_data2(void *Data, size_t Size, const char **PT)
Definition ftp.c:90
Dpip socket handler type.
Definition dpip.h:31
Definition dlib.h:102
Dstr_char_t * str
Definition dlib.h:105
int len
Definition dlib.h:104