Mercurial > gftp.yaz
annotate lib/rfc2068.c @ 209:d79e2782eb1b
2003-7-5 Brian Masney <masneyb@gftp.org>
* lib/protocols.c (gftp_get_line) - fixed bug where the read function
was being called one extra time after the end of file was reached
* lib/rfc2068.c - fixes for chunked file transfers. All known issues with the
HTTP protocol should be (hopefully) fixed now
* lib/httpcommon.h (struct rfc2068_params_tag) - added eof variable
author | masneyb |
---|---|
date | Sat, 05 Jul 2003 17:30:14 +0000 |
parents | cf4098008615 |
children | 3d6e024dbf31 |
rev | line source |
---|---|
1 | 1 /*****************************************************************************/ |
2 /* rfc2068.c - General purpose routines for the HTTP protocol */ | |
122 | 3 /* Copyright (C) 1998-2003 Brian Masney <masneyb@gftp.org> */ |
1 | 4 /* */ |
5 /* This program is free software; you can redistribute it and/or modify */ | |
6 /* it under the terms of the GNU General Public License as published by */ | |
7 /* the Free Software Foundation; either version 2 of the License, or */ | |
8 /* (at your option) any later version. */ | |
9 /* */ | |
10 /* This program is distributed in the hope that it will be useful, */ | |
11 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ | |
12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ | |
13 /* GNU General Public License for more details. */ | |
14 /* */ | |
15 /* You should have received a copy of the GNU General Public License */ | |
16 /* along with this program; if not, write to the Free Software */ | |
17 /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA */ | |
18 /*****************************************************************************/ | |
19 | |
20 #include "gftp.h" | |
168 | 21 #include "httpcommon.h" |
22 | |
33 | 23 static const char cvsid[] = "$Id$"; |
1 | 24 |
122 | 25 static gftp_config_vars config_vars[] = |
26 { | |
27 {"", N_("HTTP"), gftp_option_type_notebook, NULL, NULL, 0, NULL, | |
28 GFTP_PORT_GTK, NULL}, | |
29 | |
30 {"http_proxy_host", N_("Proxy hostname:"), | |
31 gftp_option_type_text, "", NULL, 0, | |
32 N_("Firewall hostname"), GFTP_PORT_ALL, 0}, | |
33 {"http_proxy_port", N_("Proxy port:"), | |
34 gftp_option_type_int, GINT_TO_POINTER(80), NULL, 0, | |
35 N_("Port to connect to on the firewall"), GFTP_PORT_ALL, NULL}, | |
36 {"http_proxy_username", N_("Proxy username:"), | |
37 gftp_option_type_text, "", NULL, 0, | |
38 N_("Your firewall username"), GFTP_PORT_ALL, NULL}, | |
39 {"http_proxy_password", N_("Proxy password:"), | |
40 gftp_option_type_hidetext, "", NULL, 0, | |
41 N_("Your firewall password"), GFTP_PORT_ALL, NULL}, | |
42 | |
43 {"use_http11", N_("Use HTTP/1.1"), | |
44 gftp_option_type_checkbox, GINT_TO_POINTER(1), NULL, 0, | |
45 N_("Do you want to use HTTP/1.1 or HTTP/1.0"), GFTP_PORT_ALL, NULL}, | |
46 {NULL, NULL, 0, NULL, NULL, 0, NULL, 0, NULL} | |
47 }; | |
48 | |
48 | 49 |
58 | 50 static off_t |
48 | 51 rfc2068_read_response (gftp_request * request) |
52 { | |
53 rfc2068_params * params; | |
207 | 54 char tempstr[8192]; |
168 | 55 int ret, chunked; |
48 | 56 |
57 params = request->protocol_data; | |
209 | 58 *tempstr = '\0'; |
168 | 59 chunked = 0; |
60 params->chunk_size = 0; | |
61 params->content_length = 0; | |
209 | 62 params->eof = 0; |
207 | 63 |
105 | 64 if (request->last_ftp_response) |
65 { | |
66 g_free (request->last_ftp_response); | |
67 request->last_ftp_response = NULL; | |
68 } | |
48 | 69 |
207 | 70 if (params->extra_read_buffer != NULL) |
71 { | |
72 g_free (params->extra_read_buffer); | |
73 params->extra_read_buffer = NULL; | |
209 | 74 params->extra_read_buffer_len = 0; |
207 | 75 } |
76 | |
105 | 77 do |
78 { | |
168 | 79 if ((ret = gftp_get_line (request, ¶ms->rbuf, tempstr, |
169 | 80 sizeof (tempstr), request->datafd)) < 0) |
105 | 81 return (ret); |
48 | 82 |
105 | 83 if (request->last_ftp_response == NULL) |
84 request->last_ftp_response = g_strdup (tempstr); | |
85 | |
86 if (*tempstr != '\0') | |
87 { | |
186 | 88 request->logging_function (gftp_logging_recv, request, "%s\n", |
105 | 89 tempstr); |
48 | 90 |
105 | 91 if (strncmp (tempstr, "Content-Length:", 15) == 0) |
168 | 92 params->content_length = strtol (tempstr + 16, NULL, 10); |
93 else if (strcmp (tempstr, "Transfer-Encoding: chunked") == 0) | |
94 chunked = 1; | |
105 | 95 } |
48 | 96 } |
105 | 97 while (*tempstr != '\0'); |
48 | 98 |
168 | 99 if (chunked) |
100 { | |
101 if ((ret = gftp_get_line (request, ¶ms->rbuf, tempstr, | |
169 | 102 sizeof (tempstr), request->datafd)) < 0) |
168 | 103 return (ret); |
104 | |
105 if (sscanf ((char *) tempstr, "%lx", ¶ms->chunk_size) != 1) | |
106 { | |
186 | 107 request->logging_function (gftp_logging_recv, request, |
209 | 108 _("Received wrong response from server, disconnecting\nInvalid chunk size '%s' returned by the remote server\n"), |
109 tempstr); | |
168 | 110 gftp_disconnect (request); |
111 return (GFTP_EFATAL); | |
112 } | |
113 | |
114 if (params->chunk_size == 0) | |
115 return (0); | |
116 | |
117 params->chunk_size -= params->rbuf->cur_bufsize; | |
118 if (params->chunk_size < 0) | |
119 { | |
207 | 120 params->extra_read_buffer_len = params->chunk_size * -1; |
209 | 121 params->chunk_size = 0; |
207 | 122 params->extra_read_buffer = g_malloc (params->extra_read_buffer_len + 1); |
123 memcpy (params->extra_read_buffer, params->rbuf->curpos + (params->rbuf->cur_bufsize - params->extra_read_buffer_len), params->extra_read_buffer_len); | |
124 params->extra_read_buffer[params->extra_read_buffer_len] = '\0'; | |
125 params->rbuf->cur_bufsize -= params->extra_read_buffer_len; | |
209 | 126 params->rbuf->curpos[params->rbuf->cur_bufsize] = '\0'; |
168 | 127 } |
128 } | |
129 | |
130 params->chunked_transfer = chunked; | |
131 return (params->content_length > 0 ? params->content_length : params->chunk_size); | |
48 | 132 } |
133 | |
134 | |
58 | 135 static off_t |
168 | 136 rfc2068_send_command (gftp_request * request, const void *command, size_t len) |
48 | 137 { |
122 | 138 char *tempstr, *str, *proxy_hostname, *proxy_username, *proxy_password; |
139 int proxy_port; | |
58 | 140 ssize_t ret; |
48 | 141 |
84 | 142 g_return_val_if_fail (request != NULL, GFTP_EFATAL); |
143 g_return_val_if_fail (request->protonum == GFTP_HTTP_NUM, GFTP_EFATAL); | |
144 g_return_val_if_fail (command != NULL, GFTP_EFATAL); | |
48 | 145 |
168 | 146 tempstr = g_strdup_printf ("%sUser-Agent: %s\nHost: %s\n", (char *) command, |
122 | 147 gftp_version, request->hostname); |
48 | 148 |
186 | 149 request->logging_function (gftp_logging_send, request, |
58 | 150 "%s", tempstr); |
151 | |
168 | 152 ret = request->write_function (request, tempstr, strlen (tempstr), |
169 | 153 request->datafd); |
58 | 154 g_free (tempstr); |
155 | |
156 if (ret < 0) | |
84 | 157 return (ret); |
48 | 158 |
122 | 159 gftp_lookup_request_option (request, "http_proxy_host", &proxy_hostname); |
160 gftp_lookup_request_option (request, "http_proxy_port", &proxy_port); | |
161 gftp_lookup_request_option (request, "http_proxy_username", &proxy_username); | |
162 gftp_lookup_request_option (request, "http_proxy_password", &proxy_password); | |
163 | |
164 if (request->use_proxy && proxy_username != NULL && *proxy_username != '\0') | |
48 | 165 { |
122 | 166 tempstr = g_strconcat (proxy_username, ":", proxy_password, NULL); |
48 | 167 str = base64_encode (tempstr); |
168 g_free (tempstr); | |
169 | |
186 | 170 request->logging_function (gftp_logging_send, request, |
48 | 171 "Proxy-authorization: Basic xxxx:xxxx\n"); |
169 | 172 ret = gftp_writefmt (request, request->datafd, |
58 | 173 "Proxy-authorization: Basic %s\n", str); |
48 | 174 g_free (str); |
58 | 175 if (ret < 0) |
168 | 176 return (ret); |
48 | 177 } |
178 | |
179 if (request->username != NULL && *request->username != '\0') | |
180 { | |
181 tempstr = g_strconcat (request->username, ":", request->password, NULL); | |
182 str = base64_encode (tempstr); | |
183 g_free (tempstr); | |
184 | |
186 | 185 request->logging_function (gftp_logging_send, request, |
48 | 186 "Authorization: Basic xxxx\n"); |
169 | 187 ret = gftp_writefmt (request, request->datafd, |
58 | 188 "Authorization: Basic %s\n", str); |
48 | 189 g_free (str); |
58 | 190 if (ret < 0) |
84 | 191 return (ret); |
48 | 192 } |
193 | |
169 | 194 if ((ret = request->write_function (request, "\n", 1, request->datafd)) < 0) |
84 | 195 return (ret); |
58 | 196 |
48 | 197 return (rfc2068_read_response (request)); |
1 | 198 } |
199 | |
200 | |
201 static int | |
202 rfc2068_connect (gftp_request * request) | |
203 { | |
136 | 204 char *proxy_hostname, *proxy_config; |
122 | 205 rfc2068_params * params; |
177 | 206 int proxy_port, ret; |
1 | 207 |
84 | 208 g_return_val_if_fail (request != NULL, GFTP_EFATAL); |
209 g_return_val_if_fail (request->protonum == GFTP_HTTP_NUM, GFTP_EFATAL); | |
210 g_return_val_if_fail (request->hostname != NULL, GFTP_EFATAL); | |
1 | 211 |
122 | 212 params = request->protocol_data; |
213 | |
169 | 214 if (request->datafd > 0) |
1 | 215 return (0); |
216 | |
136 | 217 gftp_lookup_request_option (request, "proxy_config", &proxy_config); |
122 | 218 gftp_lookup_request_option (request, "http_proxy_host", &proxy_hostname); |
219 gftp_lookup_request_option (request, "http_proxy_port", &proxy_port); | |
87 | 220 |
168 | 221 if (proxy_config != NULL && strcmp (proxy_config, "ftp") == 0) |
222 { | |
223 g_free (request->url_prefix); | |
224 request->url_prefix = g_strdup ("ftp"); | |
225 } | |
136 | 226 |
177 | 227 if ((ret = gftp_connect_server (request, request->url_prefix, proxy_hostname, |
228 proxy_port)) < 0) | |
229 return (ret); | |
1 | 230 |
231 if (request->directory && *request->directory == '\0') | |
232 { | |
233 g_free (request->directory); | |
234 request->directory = NULL; | |
235 } | |
236 | |
237 if (!request->directory) | |
105 | 238 request->directory = g_strdup ("/"); |
1 | 239 |
240 return (0); | |
241 } | |
242 | |
243 | |
244 static void | |
245 rfc2068_disconnect (gftp_request * request) | |
246 { | |
247 g_return_if_fail (request != NULL); | |
248 g_return_if_fail (request->protonum == GFTP_HTTP_NUM); | |
249 | |
169 | 250 if (request->datafd > 0) |
1 | 251 { |
186 | 252 request->logging_function (gftp_logging_misc, request, |
1 | 253 _("Disconnecting from site %s\n"), |
254 request->hostname); | |
14
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
7
diff
changeset
|
255 |
169 | 256 if (close (request->datafd) < 0) |
186 | 257 request->logging_function (gftp_logging_error, request, |
58 | 258 _("Error closing file descriptor: %s\n"), |
259 g_strerror (errno)); | |
260 | |
169 | 261 request->datafd = -1; |
14
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
7
diff
changeset
|
262 } |
1 | 263 } |
264 | |
265 | |
58 | 266 static off_t |
267 rfc2068_get_file (gftp_request * request, const char *filename, int fd, | |
1 | 268 off_t startsize) |
269 { | |
122 | 270 int restarted, ret, use_http11; |
168 | 271 char *tempstr, *oldstr, *pos; |
122 | 272 rfc2068_params * params; |
1 | 273 off_t size; |
274 | |
84 | 275 g_return_val_if_fail (request != NULL, GFTP_EFATAL); |
276 g_return_val_if_fail (request->protonum == GFTP_HTTP_NUM, GFTP_EFATAL); | |
277 g_return_val_if_fail (filename != NULL, GFTP_EFATAL); | |
1 | 278 |
122 | 279 params = request->protocol_data; |
280 | |
281 gftp_lookup_request_option (request, "use_http11", &use_http11); | |
282 | |
58 | 283 if (fd > 0) |
169 | 284 request->datafd = fd; |
1 | 285 |
169 | 286 if (request->datafd < 0 && (ret = rfc2068_connect (request)) != 0) |
84 | 287 return (ret); |
1 | 288 |
7 | 289 if (request->username == NULL || *request->username == '\0') |
122 | 290 tempstr = g_strconcat ("GET ", request->url_prefix, "://", |
1 | 291 request->hostname, "/", filename, |
292 use_http11 ? " HTTP/1.1\n" : " HTTP/1.0\n", NULL); | |
293 else | |
122 | 294 tempstr = g_strconcat ("GET ", request->url_prefix, "://", |
1 | 295 request->username, "@", request->hostname, "/", filename, |
296 use_http11 ? " HTTP/1.1\n" : " HTTP/1.0\n", NULL); | |
297 | |
298 if ((pos = strstr (tempstr, "://")) != NULL) | |
299 remove_double_slashes (pos + 3); | |
300 else | |
301 remove_double_slashes (tempstr); | |
302 | |
168 | 303 if (use_http11 && startsize > 0) |
1 | 304 { |
14
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
7
diff
changeset
|
305 #if defined (_LARGEFILE_SOURCE) |
186 | 306 request->logging_function (gftp_logging_misc, request, |
14
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
7
diff
changeset
|
307 _("Starting the file transfer at offset %lld\n"), |
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
7
diff
changeset
|
308 startsize); |
168 | 309 |
310 oldstr = tempstr; | |
311 tempstr = g_strdup_printf ("%sRange: bytes=%lld-\n", tempstr, startsize); | |
312 g_free (oldstr); | |
14
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
7
diff
changeset
|
313 #else |
186 | 314 request->logging_function (gftp_logging_misc, request, |
1 | 315 _("Starting the file transfer at offset %ld\n"), |
316 startsize); | |
168 | 317 |
318 oldstr = tempstr; | |
319 tempstr = g_strdup_printf ("%sRange: bytes=%ld-\n", tempstr, startsize); | |
320 g_free (oldstr); | |
14
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
7
diff
changeset
|
321 #endif |
1 | 322 } |
323 | |
168 | 324 size = rfc2068_send_command (request, tempstr, strlen (tempstr)); |
1 | 325 g_free (tempstr); |
58 | 326 if (size < 0) |
84 | 327 return (size); |
1 | 328 |
329 restarted = 0; | |
330 if (strlen (request->last_ftp_response) > 9 | |
331 && strncmp (request->last_ftp_response + 9, "206", 3) == 0) | |
332 restarted = 1; | |
14
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
7
diff
changeset
|
333 else if (strlen (request->last_ftp_response) < 9 || |
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
7
diff
changeset
|
334 strncmp (request->last_ftp_response + 9, "200", 3) != 0) |
1 | 335 { |
186 | 336 request->logging_function (gftp_logging_error, request, |
1 | 337 _("Cannot retrieve file %s\n"), filename); |
84 | 338 return (GFTP_ERETRYABLE); |
1 | 339 } |
340 | |
168 | 341 params->read_bytes = 0; |
342 | |
1 | 343 return (restarted ? size + startsize : size); |
344 } | |
345 | |
346 | |
58 | 347 static ssize_t |
1 | 348 rfc2068_get_next_file_chunk (gftp_request * request, char *buf, size_t size) |
349 { | |
350 rfc2068_params * params; | |
351 size_t len; | |
352 | |
84 | 353 g_return_val_if_fail (request != NULL, GFTP_EFATAL); |
354 g_return_val_if_fail (request->protonum == GFTP_HTTP_NUM, GFTP_EFATAL); | |
1 | 355 |
356 params = request->protocol_data; | |
357 | |
169 | 358 if ((len = request->read_function (request, buf, size, request->datafd)) < 0) |
84 | 359 return ((ssize_t) len); |
1 | 360 |
361 return (len); | |
362 } | |
363 | |
364 | |
365 static int | |
366 rfc2068_end_transfer (gftp_request * request) | |
367 { | |
368 rfc2068_params * params; | |
369 | |
84 | 370 g_return_val_if_fail (request != NULL, GFTP_EFATAL); |
371 g_return_val_if_fail (request->protonum == GFTP_HTTP_NUM, GFTP_EFATAL); | |
1 | 372 |
169 | 373 if (request->datafd < 0) |
84 | 374 return (GFTP_EFATAL); |
1 | 375 |
207 | 376 gftp_disconnect (request); |
14
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
7
diff
changeset
|
377 |
1 | 378 params = request->protocol_data; |
168 | 379 params->content_length = 0; |
380 params->chunked_transfer = 0; | |
381 params->chunk_size = 0; | |
209 | 382 params->eof = 0; |
14
83090328581e
* More largefile support. Hopefully all that is left is the configure stuff
masneyb
parents:
7
diff
changeset
|
383 |
1 | 384 return (0); |
385 } | |
386 | |
387 | |
388 static int | |
389 rfc2068_list_files (gftp_request * request) | |
390 { | |
391 rfc2068_params *params; | |
122 | 392 char *tempstr, *pos; |
393 int r, use_http11; | |
58 | 394 off_t ret; |
1 | 395 |
84 | 396 g_return_val_if_fail (request != NULL, GFTP_EFATAL); |
397 g_return_val_if_fail (request->protonum == GFTP_HTTP_NUM, GFTP_EFATAL); | |
1 | 398 |
399 params = request->protocol_data; | |
122 | 400 gftp_lookup_request_option (request, "use_http11", &use_http11); |
401 | |
169 | 402 if (request->datafd < 0 && (r = rfc2068_connect (request)) < 0) |
84 | 403 return (r); |
1 | 404 |
7 | 405 if (request->username == NULL || *request->username == '\0') |
122 | 406 tempstr = g_strconcat ("GET ", request->url_prefix, "://", |
1 | 407 request->hostname, "/", request->directory, |
408 use_http11 ? "/ HTTP/1.1\n" : "/ HTTP/1.0\n", NULL); | |
409 else | |
122 | 410 tempstr = g_strconcat ("GET ", request->url_prefix, "://", |
411 request->username, "@", request->hostname, "/", | |
412 request->directory, | |
1 | 413 use_http11 ? "/ HTTP/1.1\n" : "/ HTTP/1.0\n", NULL); |
414 | |
415 if ((pos = strstr (tempstr, "://")) != NULL) | |
416 remove_double_slashes (pos + 3); | |
417 else | |
418 remove_double_slashes (tempstr); | |
419 | |
168 | 420 ret = rfc2068_send_command (request, tempstr, strlen (tempstr)); |
1 | 421 g_free (tempstr); |
58 | 422 if (ret < 0) |
84 | 423 return ((int) ret); |
1 | 424 |
425 params->read_bytes = 0; | |
426 if (strlen (request->last_ftp_response) > 9 && | |
427 strncmp (request->last_ftp_response + 9, "200", 3) == 0) | |
428 { | |
186 | 429 request->logging_function (gftp_logging_misc, request, |
1 | 430 _("Retrieving directory listing...\n")); |
431 return (0); | |
432 } | |
168 | 433 |
207 | 434 gftp_end_transfer (request); |
435 | |
84 | 436 return (GFTP_ERETRYABLE); |
1 | 437 } |
438 | |
439 | |
440 static off_t | |
441 rfc2068_get_file_size (gftp_request * request, const char *filename) | |
442 { | |
122 | 443 rfc2068_params *params; |
444 char *tempstr, *pos; | |
445 int ret, use_http11; | |
58 | 446 off_t size; |
1 | 447 |
84 | 448 g_return_val_if_fail (request != NULL, GFTP_EFATAL); |
449 g_return_val_if_fail (request->protonum == GFTP_HTTP_NUM, GFTP_EFATAL); | |
450 g_return_val_if_fail (filename != NULL, GFTP_EFATAL); | |
1 | 451 |
122 | 452 params = request->protocol_data; |
453 gftp_lookup_request_option (request, "use_http11", &use_http11); | |
454 | |
169 | 455 if (request->datafd < 0 && (ret = rfc2068_connect (request)) != 0) |
84 | 456 return (ret); |
1 | 457 |
7 | 458 if (request->username == NULL || *request->username == '\0') |
122 | 459 tempstr = g_strconcat ("HEAD ", request->url_prefix, request->hostname, "/", |
460 filename, | |
1 | 461 use_http11 ? " HTTP/1.1\n" : " HTTP/1.0\n", NULL); |
462 else | |
122 | 463 tempstr = g_strconcat ("HEAD ", request->url_prefix, request->username, |
464 "@", request->hostname, "/", filename, | |
1 | 465 use_http11 ? " HTTP/1.1\n" : " HTTP/1.0\n", NULL); |
466 | |
467 if ((pos = strstr (tempstr, "://")) != NULL) | |
468 remove_double_slashes (pos + 3); | |
469 else | |
470 remove_double_slashes (tempstr); | |
471 | |
168 | 472 size = rfc2068_send_command (request, tempstr, strlen (tempstr)); |
1 | 473 g_free (tempstr); |
474 return (size); | |
475 } | |
476 | |
477 | |
478 static int | |
479 parse_html_line (char *tempstr, gftp_file * fle) | |
480 { | |
207 | 481 char *stpos, *kpos, *mpos, *pos; |
1 | 482 long units; |
483 | |
484 memset (fle, 0, sizeof (*fle)); | |
485 | |
486 if ((pos = strstr (tempstr, "<A HREF=")) == NULL && | |
487 (pos = strstr (tempstr, "<a href=")) == NULL) | |
488 return (0); | |
489 | |
490 /* Find the filename */ | |
491 while (*pos != '"' && *pos != '\0') | |
492 pos++; | |
493 if (*pos == '\0') | |
494 return (0); | |
495 pos++; | |
496 | |
497 for (stpos = pos; *pos != '"' && *pos != '\0'; pos++); | |
498 if (*pos == '\0') | |
499 return (0); | |
500 *pos = '\0'; | |
501 | |
502 /* Copy file attributes. Just about the only thing we can get is whether it | |
503 is a directory or not */ | |
105 | 504 fle->attribs = g_strdup ("----------"); |
1 | 505 if (*(pos - 1) == '/') |
506 { | |
507 *(pos - 1) = '\0'; | |
508 *fle->attribs = 'd'; | |
509 fle->isdir = 1; | |
510 } | |
511 | |
512 /* Copy filename */ | |
513 if (strchr (stpos, '/') != NULL || strncmp (stpos, "mailto:", 7) == 0 || | |
514 *stpos == '\0' || *stpos == '?') | |
515 return (0); | |
516 | |
105 | 517 fle->file = g_strdup (stpos); |
1 | 518 |
519 if (*(pos - 1) == '\0') | |
520 *(pos - 1) = '/'; | |
521 *pos = '"'; | |
522 pos++; | |
523 | |
524 /* Skip whitespace and html tags after file and before date */ | |
525 stpos = pos; | |
526 if ((pos = strstr (stpos, "</A>")) == NULL && | |
527 (pos = strstr (stpos, "</a>")) == NULL) | |
528 return (0); | |
529 | |
530 pos += 4; | |
531 | |
105 | 532 while (*pos == ' ' || *pos == '\t' || *pos == '.' || *pos == '<') |
1 | 533 { |
534 if (*pos == '<') | |
535 { | |
536 if (strncmp (pos, "<A ", 3) == 0 || strncmp (pos, "<a ", 3) == 0) | |
537 { | |
538 stpos = pos; | |
539 if ((pos = strstr (stpos, "</A>")) == NULL | |
540 && (pos = strstr (stpos, "</a>")) == NULL) | |
541 return (0); | |
542 pos += 4; | |
543 } | |
544 else | |
545 { | |
546 while (*pos != '>' && *pos != '\0') | |
547 pos++; | |
548 if (*pos == '\0') | |
549 return (0); | |
550 } | |
551 } | |
552 pos++; | |
553 } | |
554 | |
102 | 555 if (*pos == '[') |
556 pos++; | |
1 | 557 |
102 | 558 fle->datetime = parse_time (pos, &pos); |
1 | 559 |
168 | 560 fle->user = g_strdup (_("<unknown>")); |
561 fle->group = g_strdup (_("<unknown>")); | |
562 | |
105 | 563 if (pos == NULL) |
564 return (1); | |
565 | |
1 | 566 while (*pos == ' ' || *pos == ']') |
567 pos++; | |
568 | |
569 /* Get the size */ | |
207 | 570 |
571 kpos = strchr (pos, 'k'); | |
572 mpos = strchr (pos, 'M'); | |
573 if (kpos == NULL) | |
574 stpos = mpos; | |
575 else if (mpos == NULL) | |
576 stpos = kpos; | |
577 else if (kpos < stpos) | |
578 stpos = kpos; | |
579 else | |
580 stpos = mpos; | |
581 | |
1 | 582 if (stpos == NULL || !isdigit (*(stpos - 1))) |
583 return (1); /* Return successfully | |
584 since we got the file */ | |
207 | 585 |
1 | 586 if (*stpos == 'k') |
587 units = 1024; | |
588 else | |
589 units = 1048576; | |
590 | |
591 fle->size = 0; | |
592 | |
593 while (*(stpos - 1) != ' ' && *(stpos - 1) != '\t' && stpos > tempstr) | |
594 { | |
595 stpos--; | |
207 | 596 if ((*stpos == '.') && isdigit (*(stpos + 1))) |
1 | 597 { /* found decimal point */ |
207 | 598 fle->size = units * strtol (stpos + 1, NULL, 10) / 10;; |
1 | 599 } |
600 } | |
601 | |
602 fle->size += units * strtol (stpos, NULL, 10); | |
168 | 603 |
1 | 604 return (1); |
605 } | |
606 | |
607 | |
168 | 608 int |
58 | 609 rfc2068_get_next_file (gftp_request * request, gftp_file * fle, int fd) |
1 | 610 { |
48 | 611 rfc2068_params * params; |
612 char tempstr[255]; | |
168 | 613 ssize_t len; |
84 | 614 int ret; |
1 | 615 |
84 | 616 g_return_val_if_fail (request != NULL, GFTP_EFATAL); |
617 g_return_val_if_fail (request->protonum == GFTP_HTTP_NUM, GFTP_EFATAL); | |
618 g_return_val_if_fail (fle != NULL, GFTP_EFATAL); | |
48 | 619 |
620 params = request->protocol_data; | |
621 if (request->last_dir_entry) | |
622 { | |
623 g_free (request->last_dir_entry); | |
624 request->last_dir_entry = NULL; | |
625 } | |
626 | |
105 | 627 if (fd < 0) |
169 | 628 fd = request->datafd; |
105 | 629 |
48 | 630 while (1) |
631 { | |
168 | 632 if ((ret = gftp_get_line (request, ¶ms->rbuf, tempstr, sizeof (tempstr), fd)) <= 0) |
84 | 633 return (ret); |
1 | 634 |
48 | 635 if (parse_html_line (tempstr, fle) == 0 || fle->file == NULL) |
636 gftp_file_destroy (fle); | |
637 else | |
638 break; | |
639 } | |
640 | |
641 if (fle->file == NULL) | |
1 | 642 { |
48 | 643 gftp_file_destroy (fle); |
105 | 644 return (0); |
1 | 645 } |
646 | |
48 | 647 len = strlen (tempstr); |
648 if (!request->cached) | |
649 { | |
60 | 650 request->last_dir_entry = g_strdup_printf ("%s\n", tempstr); |
651 request->last_dir_entry_len = len + 1; | |
48 | 652 } |
58 | 653 return (len); |
48 | 654 } |
1 | 655 |
656 | |
48 | 657 static int |
658 rfc2068_chdir (gftp_request * request, const char *directory) | |
659 { | |
204 | 660 char *tempstr, *olddir; |
661 | |
84 | 662 g_return_val_if_fail (request != NULL, GFTP_EFATAL); |
663 g_return_val_if_fail (request->protonum == GFTP_HTTP_NUM, GFTP_EFATAL); | |
664 g_return_val_if_fail (directory != NULL, GFTP_EFATAL); | |
48 | 665 |
666 if (request->directory != directory) | |
667 { | |
204 | 668 olddir = request->directory; |
669 | |
670 if (*directory != '/') | |
671 { | |
672 tempstr = g_strconcat (request->directory, "/", directory, NULL); | |
673 request->directory = expand_path (tempstr); | |
674 g_free (tempstr); | |
675 } | |
676 else | |
677 request->directory = expand_path (directory); | |
678 | |
679 if (olddir != NULL) | |
680 g_free (olddir); | |
48 | 681 } |
682 return (0); | |
683 } | |
684 | |
685 | |
177 | 686 static int |
58 | 687 rfc2068_set_config_options (gftp_request * request) |
688 { | |
177 | 689 return (0); |
58 | 690 } |
691 | |
692 | |
87 | 693 static void |
694 rfc2068_destroy (gftp_request * request) | |
695 { | |
207 | 696 rfc2068_params * params; |
697 | |
698 params = request->protocol_data; | |
699 | |
87 | 700 if (request->url_prefix) |
701 { | |
702 g_free (request->url_prefix); | |
703 request->url_prefix = NULL; | |
704 } | |
207 | 705 |
706 if (params->rbuf != NULL) | |
707 gftp_free_getline_buffer (¶ms->rbuf); | |
708 | |
709 if (params->extra_read_buffer != NULL) | |
710 { | |
711 g_free (params->extra_read_buffer); | |
712 params->extra_read_buffer = NULL; | |
209 | 713 params->extra_read_buffer_len = 0; |
207 | 714 } |
87 | 715 } |
716 | |
717 | |
168 | 718 static ssize_t |
719 rfc2068_chunked_read (gftp_request * request, void *ptr, size_t size, int fd) | |
720 { | |
209 | 721 size_t read_size, begin_ptr_len; |
168 | 722 rfc2068_params * params; |
723 char *stpos, *endpos; | |
209 | 724 void *read_ptr_pos; |
168 | 725 ssize_t retval; |
726 | |
727 params = request->protocol_data; | |
728 | |
207 | 729 if (params->extra_read_buffer != NULL) |
168 | 730 { |
209 | 731 g_return_val_if_fail (params->extra_read_buffer_len <= size, GFTP_EFATAL); |
732 | |
733 memcpy (ptr, params->extra_read_buffer, params->extra_read_buffer_len); | |
734 | |
735 begin_ptr_len = params->extra_read_buffer_len; | |
736 read_ptr_pos = (char *) ptr + begin_ptr_len; | |
737 | |
738 /* Check for end of chunk */ | |
739 if (begin_ptr_len > 5 && strncmp ("\r\n0\r\n", (char *) ptr, 5) == 0) | |
740 read_size = 0; | |
741 else | |
742 read_size = size - begin_ptr_len; | |
743 | |
744 g_free (params->extra_read_buffer); | |
745 params->extra_read_buffer = NULL; | |
746 params->extra_read_buffer_len = 0; | |
207 | 747 } |
748 else | |
749 { | |
209 | 750 begin_ptr_len = 0; |
751 read_ptr_pos = ptr; | |
752 | |
207 | 753 read_size = size; |
754 if (params->content_length > 0) | |
755 { | |
756 if (params->content_length == params->read_bytes) | |
757 return (0); | |
168 | 758 |
207 | 759 if (read_size + params->read_bytes > params->content_length) |
760 read_size = params->content_length - params->read_bytes; | |
761 } | |
762 else if (params->chunked_transfer && params->chunk_size > 0 && | |
763 params->chunk_size < read_size) | |
764 read_size = params->chunk_size; | |
168 | 765 } |
766 | |
209 | 767 if (read_size > 0 && !params->eof) |
768 { | |
769 retval = params->real_read_function (request, read_ptr_pos, read_size, fd); | |
770 | |
771 if (retval > 0) | |
772 params->read_bytes += retval; | |
773 else if (retval == 0) | |
774 params->eof = 1; | |
775 else if (retval < 0) | |
776 return (retval); | |
777 | |
778 if (params->chunk_size > 0 && retval > 0) | |
779 { | |
780 params->chunk_size -= retval; | |
781 return (retval); | |
782 } | |
783 | |
784 retval += begin_ptr_len; | |
785 } | |
786 else | |
787 retval = begin_ptr_len; | |
168 | 788 |
789 if (!params->chunked_transfer || retval <= 0) | |
790 return (retval); | |
791 | |
209 | 792 stpos = (char *) ptr; |
793 while (params->chunk_size == 0) | |
168 | 794 { |
209 | 795 if (*stpos != '\r' || *(stpos + 1) != '\n') |
168 | 796 { |
209 | 797 request->logging_function (gftp_logging_recv, request, |
798 _("Received wrong response from server, disconnecting\nExpecting a carriage return and line feed before the chunk size in the server response\n")); | |
799 gftp_disconnect (request); | |
800 return (GFTP_EFATAL); | |
801 } | |
802 | |
803 for (endpos = stpos + 2; | |
804 *endpos != '\n' && endpos < stpos + retval; | |
805 endpos++); | |
168 | 806 |
209 | 807 if (*endpos != '\n') |
808 { | |
809 /* The current chunk size is split between multiple packets. | |
810 Save this chunk and read the next */ | |
207 | 811 |
209 | 812 params->extra_read_buffer = g_malloc (retval + 1); |
813 memcpy (params->extra_read_buffer, ptr, retval); | |
814 params->extra_read_buffer[retval] = '\0'; | |
815 params->extra_read_buffer_len = retval; | |
816 return (rfc2068_chunked_read (request, ptr, size, fd)); | |
817 } | |
168 | 818 |
209 | 819 *endpos = '\0'; |
820 if (*(endpos - 1) == '\r') | |
821 *(endpos - 1) = '\0'; | |
822 | |
823 if (sscanf (stpos + 2, "%lx", ¶ms->chunk_size) != 1) | |
824 { | |
825 request->logging_function (gftp_logging_recv, request, | |
826 _("Received wrong response from server, disconnecting\nInvalid chunk size '%s' returned by the remote server\n"), | |
827 stpos + 2); | |
828 gftp_disconnect (request); | |
829 return (GFTP_EFATAL); | |
830 } | |
831 | |
832 if (params->chunk_size == 0) | |
833 { | |
834 if (params->eof) | |
168 | 835 return (0); |
209 | 836 |
837 params->eof = 1; | |
838 return (retval); | |
839 } | |
840 | |
841 retval -= endpos - (char *) stpos + 1; | |
168 | 842 |
209 | 843 memmove (stpos, endpos + 1, retval - (stpos - (char *) ptr)); |
844 | |
845 params->chunk_size -= retval; | |
846 if (params->chunk_size < 0) | |
847 { | |
848 stpos += retval + params->chunk_size; /* chunk size is negative */ | |
849 params->chunk_size = 0; | |
168 | 850 } |
851 } | |
852 | |
853 return (retval); | |
854 } | |
855 | |
856 | |
122 | 857 void |
858 rfc2068_register_module (void) | |
859 { | |
860 gftp_register_config_vars (config_vars); | |
861 } | |
862 | |
863 | |
173 | 864 int |
48 | 865 rfc2068_init (gftp_request * request) |
866 { | |
168 | 867 rfc2068_params * params; |
868 | |
173 | 869 g_return_val_if_fail (request != NULL, GFTP_EFATAL); |
1 | 870 |
48 | 871 request->protonum = GFTP_HTTP_NUM; |
872 request->init = rfc2068_init; | |
168 | 873 request->read_function = rfc2068_chunked_read; |
874 request->write_function = gftp_fd_write; | |
87 | 875 request->destroy = rfc2068_destroy; |
48 | 876 request->connect = rfc2068_connect; |
168 | 877 request->post_connect = NULL; |
48 | 878 request->disconnect = rfc2068_disconnect; |
879 request->get_file = rfc2068_get_file; | |
880 request->put_file = NULL; | |
881 request->transfer_file = NULL; | |
882 request->get_next_file_chunk = rfc2068_get_next_file_chunk; | |
883 request->put_next_file_chunk = NULL; | |
884 request->end_transfer = rfc2068_end_transfer; | |
885 request->abort_transfer = rfc2068_end_transfer; /* NOTE: uses end_transfer */ | |
886 request->list_files = rfc2068_list_files; | |
887 request->get_next_file = rfc2068_get_next_file; | |
888 request->get_file_size = rfc2068_get_file_size; | |
889 request->chdir = rfc2068_chdir; | |
890 request->rmdir = NULL; | |
891 request->rmfile = NULL; | |
892 request->mkdir = NULL; | |
893 request->rename = NULL; | |
894 request->chmod = NULL; | |
895 request->site = NULL; | |
58 | 896 request->parse_url = NULL; |
63 | 897 request->swap_socks = NULL; |
58 | 898 request->set_config_options = rfc2068_set_config_options; |
87 | 899 request->url_prefix = g_strdup ("http"); |
48 | 900 request->need_hostport = 1; |
901 request->need_userpass = 0; | |
902 request->use_cache = 1; | |
903 request->use_threads = 1; | |
105 | 904 request->always_connected = 1; |
168 | 905 |
48 | 906 request->protocol_data = g_malloc0 (sizeof (rfc2068_params)); |
168 | 907 params = request->protocol_data; |
908 params->real_read_function = gftp_fd_read; | |
909 | |
177 | 910 return (gftp_set_config_options (request)); |
48 | 911 } |
1 | 912 |