1 /* The MIT License
2
3 Copyright (c) 2008 by Genome Research Ltd (GRL).
4 2010 by Attractive Chaos <attractor@live.co.uk>
5
6 Permission is hereby granted, free of charge, to any person obtaining
7 a copy of this software and associated documentation files (the
8 "Software"), to deal in the Software without restriction, including
9 without limitation the rights to use, copy, modify, merge, publish,
10 distribute, sublicense, and/or sell copies of the Software, and to
11 permit persons to whom the Software is furnished to do so, subject to
12 the following conditions:
13
14 The above copyright notice and this permission notice shall be
15 included in all copies or substantial portions of the Software.
16
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 SOFTWARE.
25 */
26
27 /* Probably I will not do socket programming in the next few years and
28 therefore I decide to heavily annotate this file, for Linux and
29 Windows as well. -ac */
30
31 #include <config.h>
32
33 #include <time.h>
34 #include <stdio.h>
35 #include <ctype.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <unistd.h>
40 #include <sys/types.h>
41
42 #ifndef _WIN32
43 #include <netdb.h>
44 #include <arpa/inet.h>
45 #include <sys/socket.h>
46 #include <sys/select.h>
47 #endif
48
49 #include "htslib/knetfile.h"
50
51 /* In winsock.h, the type of a socket is SOCKET, which is: "typedef
52 * u_int SOCKET". An invalid SOCKET is: "(SOCKET)(~0)", or signed
53 * integer -1. In knetfile.c, I use "int" for socket type
54 * throughout. This should be improved to avoid confusion.
55 *
56 * In Linux/Mac, recv() and read() do almost the same thing. You can see
57 * in the header file that netread() is simply an alias of read(). In
58 * Windows, however, they are different and using recv() is mandatory.
59 */
60
61 /* This function tests if the file handler is ready for reading (or
62 * writing if is_read==0). */
socket_wait(int fd,int is_read)63 static int socket_wait(int fd, int is_read)
64 {
65 fd_set fds, *fdr = 0, *fdw = 0;
66 struct timeval tv;
67 int ret;
68 tv.tv_sec = 5; tv.tv_usec = 0; // 5 seconds time out
69 FD_ZERO(&fds);
70 FD_SET(fd, &fds);
71 if (is_read) fdr = &fds;
72 else fdw = &fds;
73 ret = select(fd+1, fdr, fdw, 0, &tv);
74 #ifndef _WIN32
75 if (ret == -1) perror("select");
76 #else
77 if (ret == 0)
78 fprintf(stderr, "select time-out\n");
79 else if (ret == SOCKET_ERROR)
80 fprintf(stderr, "select: %d\n", WSAGetLastError());
81 #endif
82 return ret;
83 }
84
85 #ifndef _WIN32
86 /* This function does not work with Windows due to the lack of
87 * getaddrinfo() in winsock. It is addapted from an example in "Beej's
88 * Guide to Network Programming" (http://beej.us/guide/bgnet/). */
socket_connect(const char * host,const char * port)89 static int socket_connect(const char *host, const char *port)
90 {
91 #define __err_connect(func) do { perror(func); freeaddrinfo(res); return -1; } while (0)
92
93 int ai_err, on = 1, fd;
94 struct linger lng = { 0, 0 };
95 struct addrinfo hints, *res = 0;
96 memset(&hints, 0, sizeof(struct addrinfo));
97 hints.ai_family = AF_UNSPEC;
98 hints.ai_socktype = SOCK_STREAM;
99 /* In Unix/Mac, getaddrinfo() is the most convenient way to get
100 * server information. */
101 if ((ai_err = getaddrinfo(host, port, &hints, &res)) != 0) { fprintf(stderr, "can't resolve %s:%s: %s\n", host, port, gai_strerror(ai_err)); return -1; }
102 if ((fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) __err_connect("socket");
103 /* The following two setsockopt() are used by ftplib
104 * (http://nbpfaus.net/~pfau/ftplib/). I am not sure if they
105 * necessary. */
106 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) __err_connect("setsockopt");
107 if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &lng, sizeof(lng)) == -1) __err_connect("setsockopt");
108 if (connect(fd, res->ai_addr, res->ai_addrlen) != 0) __err_connect("connect");
109 freeaddrinfo(res);
110 return fd;
111 }
112 #else
113 /* MinGW's printf has problem with "%lld" */
int64tostr(char * buf,int64_t x)114 char *int64tostr(char *buf, int64_t x)
115 {
116 int cnt;
117 int i = 0;
118 do {
119 buf[i++] = '0' + x % 10;
120 x /= 10;
121 } while (x);
122 buf[i] = 0;
123 for (cnt = i, i = 0; i < cnt/2; ++i) {
124 int c = buf[i]; buf[i] = buf[cnt-i-1]; buf[cnt-i-1] = c;
125 }
126 return buf;
127 }
128
strtoint64(const char * buf)129 int64_t strtoint64(const char *buf)
130 {
131 int64_t x;
132 for (x = 0; *buf != '\0'; ++buf)
133 x = x * 10 + ((int64_t) *buf - 48);
134 return x;
135 }
136 /* In windows, the first thing is to establish the TCP connection. */
knet_win32_init()137 int knet_win32_init()
138 {
139 WSADATA wsaData;
140 return WSAStartup(MAKEWORD(2, 2), &wsaData);
141 }
knet_win32_destroy()142 void knet_win32_destroy()
143 {
144 WSACleanup();
145 }
146 /* A slightly modfied version of the following function also works on
147 * Mac (and presummably Linux). However, this function is not stable on
148 * my Mac. It sometimes works fine but sometimes does not. Therefore for
149 * non-Windows OS, I do not use this one. */
socket_connect(const char * host,const char * port)150 static SOCKET socket_connect(const char *host, const char *port)
151 {
152 #define __err_connect(func) \
153 do { \
154 fprintf(stderr, "%s: %d\n", func, WSAGetLastError()); \
155 return -1; \
156 } while (0)
157
158 int on = 1;
159 SOCKET fd;
160 struct linger lng = { 0, 0 };
161 struct sockaddr_in server;
162 struct hostent *hp = 0;
163 // open socket
164 if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) __err_connect("socket");
165 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)) == -1) __err_connect("setsockopt");
166 if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&lng, sizeof(lng)) == -1) __err_connect("setsockopt");
167 // get host info
168 if (isalpha(host[0])) hp = gethostbyname(host);
169 else {
170 struct in_addr addr;
171 addr.s_addr = inet_addr(host);
172 hp = gethostbyaddr((char*)&addr, 4, AF_INET);
173 }
174 if (hp == 0) __err_connect("gethost");
175 // connect
176 server.sin_addr.s_addr = *((unsigned long*)hp->h_addr);
177 server.sin_family= AF_INET;
178 server.sin_port = htons(atoi(port));
179 if (connect(fd, (struct sockaddr*)&server, sizeof(server)) != 0) __err_connect("connect");
180 // freehostent(hp); // strangely in MSDN, hp is NOT freed (memory leak?!)
181 return fd;
182 }
183 #endif
184
my_netread(int fd,void * buf,off_t len)185 static off_t my_netread(int fd, void *buf, off_t len)
186 {
187 off_t rest = len, curr, l = 0;
188 /* recv() and read() may not read the required length of data with
189 * one call. They have to be called repeatedly. */
190 while (rest) {
191 if (socket_wait(fd, 1) <= 0) break; // socket is not ready for reading
192 curr = netread(fd, (void*)((char*)buf + l), rest);
193 /* According to the glibc manual, section 13.2, a zero returned
194 * value indicates end-of-file (EOF), which should mean that
195 * read() will not return zero if EOF has not been met but data
196 * are not immediately available. */
197 if (curr == 0) break;
198 l += curr; rest -= curr;
199 }
200 return l;
201 }
202
203 /*************************
204 * FTP specific routines *
205 *************************/
206
kftp_get_response(knetFile * ftp)207 static int kftp_get_response(knetFile *ftp)
208 {
209 #ifndef _WIN32
210 unsigned char c;
211 #else
212 char c;
213 #endif
214 int n = 0;
215 char *p;
216 if (socket_wait(ftp->ctrl_fd, 1) <= 0) return 0;
217 while (netread(ftp->ctrl_fd, &c, 1)) { // FIXME: this is *VERY BAD* for unbuffered I/O
218 //fputc(c, stderr);
219 if (n >= ftp->max_response) {
220 ftp->max_response = ftp->max_response? ftp->max_response<<1 : 256;
221 ftp->response = (char*)realloc(ftp->response, ftp->max_response);
222 }
223 ftp->response[n++] = c;
224 if (c == '\n') {
225 if (n >= 4 && isdigit(ftp->response[0]) && isdigit(ftp->response[1]) && isdigit(ftp->response[2])
226 && ftp->response[3] != '-') break;
227 n = 0;
228 continue;
229 }
230 }
231 if (n < 2) return -1;
232 ftp->response[n-2] = 0;
233 return strtol(ftp->response, &p, 0);
234 }
235
kftp_send_cmd(knetFile * ftp,const char * cmd,int is_get)236 static int kftp_send_cmd(knetFile *ftp, const char *cmd, int is_get)
237 {
238 if (socket_wait(ftp->ctrl_fd, 0) <= 0) return -1; // socket is not ready for writing
239 int len = strlen(cmd);
240 if ( netwrite(ftp->ctrl_fd, cmd, len) != len ) return -1;
241 return is_get? kftp_get_response(ftp) : 0;
242 }
243
kftp_pasv_prep(knetFile * ftp)244 static int kftp_pasv_prep(knetFile *ftp)
245 {
246 char *p;
247 int v[6];
248 kftp_send_cmd(ftp, "PASV\r\n", 1);
249 for (p = ftp->response; *p && *p != '('; ++p);
250 if (*p != '(') return -1;
251 ++p;
252 sscanf(p, "%d,%d,%d,%d,%d,%d", &v[0], &v[1], &v[2], &v[3], &v[4], &v[5]);
253 memcpy(ftp->pasv_ip, v, 4 * sizeof(int));
254 ftp->pasv_port = (v[4]<<8&0xff00) + v[5];
255 return 0;
256 }
257
258
kftp_pasv_connect(knetFile * ftp)259 static int kftp_pasv_connect(knetFile *ftp)
260 {
261 char host[80], port[10];
262 if (ftp->pasv_port == 0) {
263 fprintf(stderr, "[kftp_pasv_connect] kftp_pasv_prep() is not called before hand.\n");
264 return -1;
265 }
266 sprintf(host, "%d.%d.%d.%d", ftp->pasv_ip[0], ftp->pasv_ip[1], ftp->pasv_ip[2], ftp->pasv_ip[3]);
267 sprintf(port, "%d", ftp->pasv_port);
268 ftp->fd = socket_connect(host, port);
269 if (ftp->fd == -1) return -1;
270 return 0;
271 }
272
kftp_connect(knetFile * ftp)273 int kftp_connect(knetFile *ftp)
274 {
275 ftp->ctrl_fd = socket_connect(ftp->host, ftp->port);
276 if (ftp->ctrl_fd == -1) return -1;
277 kftp_get_response(ftp);
278 kftp_send_cmd(ftp, "USER anonymous\r\n", 1);
279 kftp_send_cmd(ftp, "PASS kftp@\r\n", 1);
280 kftp_send_cmd(ftp, "TYPE I\r\n", 1);
281 return 0;
282 }
283
kftp_reconnect(knetFile * ftp)284 int kftp_reconnect(knetFile *ftp)
285 {
286 if (ftp->ctrl_fd != -1) {
287 netclose(ftp->ctrl_fd);
288 ftp->ctrl_fd = -1;
289 }
290 netclose(ftp->fd);
291 ftp->fd = -1;
292 return kftp_connect(ftp);
293 }
294
295 // initialize ->type, ->host, ->retr and ->size
kftp_parse_url(const char * fn,const char * mode)296 knetFile *kftp_parse_url(const char *fn, const char *mode)
297 {
298 knetFile *fp;
299 char *p;
300 int l;
301 if (strstr(fn, "ftp://") != fn) return 0;
302 for (p = (char*)fn + 6; *p && *p != '/'; ++p);
303 if (*p != '/') return 0;
304 l = p - fn - 6;
305 fp = (knetFile*)calloc(1, sizeof(knetFile));
306 fp->type = KNF_TYPE_FTP;
307 fp->fd = -1;
308 /* the Linux/Mac version of socket_connect() also recognizes a port
309 * like "ftp", but the Windows version does not. */
310 fp->port = strdup("21");
311 fp->host = (char*)calloc(l + 1, 1);
312 if (strchr(mode, 'c')) fp->no_reconnect = 1;
313 strncpy(fp->host, fn + 6, l);
314 fp->retr = (char*)calloc(strlen(p) + 8, 1);
315 sprintf(fp->retr, "RETR %s\r\n", p);
316 fp->size_cmd = (char*)calloc(strlen(p) + 8, 1);
317 sprintf(fp->size_cmd, "SIZE %s\r\n", p);
318 fp->seek_offset = 0;
319 return fp;
320 }
321 // place ->fd at offset off
kftp_connect_file(knetFile * fp)322 int kftp_connect_file(knetFile *fp)
323 {
324 int ret;
325 long long file_size;
326 if (fp->fd != -1) {
327 netclose(fp->fd);
328 if (fp->no_reconnect) kftp_get_response(fp);
329 }
330 kftp_pasv_prep(fp);
331 kftp_send_cmd(fp, fp->size_cmd, 1);
332 #ifndef _WIN32
333 // If the file does not exist, the response will be "550 Could not get file
334 // size". Be silent on failure, hts_idx_load can be trying the existence of .csi or .tbi.
335 if ( sscanf(fp->response,"%*d %lld", &file_size) != 1 ) return -1;
336 #else
337 const char *p = fp->response;
338 while (*p != ' ') ++p;
339 while (*p < '0' || *p > '9') ++p;
340 file_size = strtoint64(p);
341 #endif
342 fp->file_size = file_size;
343 if (fp->offset>=0) {
344 char tmp[32];
345 #ifndef _WIN32
346 sprintf(tmp, "REST %lld\r\n", (long long)fp->offset);
347 #else
348 strcpy(tmp, "REST ");
349 int64tostr(tmp + 5, fp->offset);
350 strcat(tmp, "\r\n");
351 #endif
352 kftp_send_cmd(fp, tmp, 1);
353 }
354 kftp_send_cmd(fp, fp->retr, 0);
355 kftp_pasv_connect(fp);
356 ret = kftp_get_response(fp);
357 if (ret != 150) {
358 fprintf(stderr, "[kftp_connect_file] %s\n", fp->response);
359 netclose(fp->fd);
360 fp->fd = -1;
361 return -1;
362 }
363 fp->is_ready = 1;
364 return 0;
365 }
366
367
368 /**************************
369 * HTTP specific routines *
370 **************************/
371
khttp_parse_url(const char * fn,const char * mode)372 knetFile *khttp_parse_url(const char *fn, const char *mode)
373 {
374 knetFile *fp;
375 char *p, *proxy, *q;
376 int l;
377 if (strstr(fn, "http://") != fn) return 0;
378 // set ->http_host
379 for (p = (char*)fn + 7; *p && *p != '/'; ++p);
380 l = p - fn - 7;
381 fp = (knetFile*)calloc(1, sizeof(knetFile));
382 fp->http_host = (char*)calloc(l + 1, 1);
383 strncpy(fp->http_host, fn + 7, l);
384 fp->http_host[l] = 0;
385 for (q = fp->http_host; *q && *q != ':'; ++q);
386 if (*q == ':') *q++ = 0;
387 // get http_proxy
388 proxy = getenv("http_proxy");
389 // set ->host, ->port and ->path
390 if (proxy == 0) {
391 fp->host = strdup(fp->http_host); // when there is no proxy, server name is identical to http_host name.
392 fp->port = strdup(*q? q : "80");
393 fp->path = strdup(*p? p : "/");
394 } else {
395 fp->host = (strstr(proxy, "http://") == proxy)? strdup(proxy + 7) : strdup(proxy);
396 for (q = fp->host; *q && *q != ':'; ++q);
397 if (*q == ':') *q++ = 0;
398 fp->port = strdup(*q? q : "80");
399 fp->path = strdup(fn);
400 }
401 fp->type = KNF_TYPE_HTTP;
402 fp->ctrl_fd = fp->fd = -1;
403 fp->seek_offset = 0;
404 return fp;
405 }
406
khttp_connect_file(knetFile * fp)407 int khttp_connect_file(knetFile *fp)
408 {
409 int ret, l = 0;
410 char *buf, *p;
411 if (fp->fd != -1) netclose(fp->fd);
412 fp->fd = socket_connect(fp->host, fp->port);
413 buf = (char*)calloc(0x10000, 1); // FIXME: I am lazy... But in principle, 64KB should be large enough.
414 l += sprintf(buf + l, "GET %s HTTP/1.0\r\nHost: %s\r\n", fp->path, fp->http_host);
415 if (fp->offset != 0) l += sprintf(buf + l, "Range: bytes=%lld-\r\n", (long long)fp->offset);
416 l += sprintf(buf + l, "\r\n");
417 if ( netwrite(fp->fd, buf, l) != l ) { free(buf); return -1; }
418 l = 0;
419 while (netread(fp->fd, buf + l, 1)) { // read HTTP header; FIXME: bad efficiency
420 if (buf[l] == '\n' && l >= 3)
421 if (strncmp(buf + l - 3, "\r\n\r\n", 4) == 0) break;
422 ++l;
423 }
424 buf[l] = 0;
425 if (l < 14) { // prematured header
426 free(buf);
427 netclose(fp->fd);
428 fp->fd = -1;
429 return -1;
430 }
431 ret = strtol(buf + 8, &p, 0); // HTTP return code
432 if (ret == 200 && fp->offset>0) { // 200 (complete result); then skip beginning of the file
433 off_t rest = fp->offset;
434 while (rest) {
435 off_t l = rest < 0x10000? rest : 0x10000;
436 rest -= my_netread(fp->fd, buf, l);
437 }
438 } else if (ret != 206 && ret != 200) {
439 // failed to open file
440 free(buf);
441 netclose(fp->fd);
442 switch (ret) {
443 case 401: errno = EPERM; break;
444 case 403: errno = EACCES; break;
445 case 404: errno = ENOENT; break;
446 case 407: errno = EPERM; break;
447 case 408: errno = ETIMEDOUT; break;
448 case 410: errno = ENOENT; break;
449 case 503: errno = EAGAIN; break;
450 case 504: errno = ETIMEDOUT; break;
451 default: errno = (ret >= 400 && ret < 500)? EINVAL : EIO; break;
452 }
453 fp->fd = -1;
454 return -1;
455 }
456 free(buf);
457 fp->is_ready = 1;
458 return 0;
459 }
460
461 /********************
462 * Generic routines *
463 ********************/
464
knet_open(const char * fn,const char * mode)465 knetFile *knet_open(const char *fn, const char *mode)
466 {
467 knetFile *fp = 0;
468 if (mode[0] != 'r') {
469 fprintf(stderr, "[knet_open] only mode \"r\" is supported.\n");
470 errno = ENOTSUP;
471 return 0;
472 }
473 if (strstr(fn, "ftp://") == fn) {
474 fp = kftp_parse_url(fn, mode);
475 if (fp == 0) return 0;
476 if (kftp_connect(fp) == -1) {
477 knet_close(fp);
478 return 0;
479 }
480 kftp_connect_file(fp);
481 } else if (strstr(fn, "http://") == fn) {
482 fp = khttp_parse_url(fn, mode);
483 if (fp == 0) return 0;
484 khttp_connect_file(fp);
485 } else { // local file
486 #ifdef _WIN32
487 /* In windows, O_BINARY is necessary. In Linux/Mac, O_BINARY may
488 * be undefined on some systems, although it is defined on my
489 * Mac and the Linux I have tested on. */
490 int fd = open(fn, O_RDONLY | O_BINARY);
491 #else
492 int fd = open(fn, O_RDONLY);
493 #endif
494 if (fd == -1) {
495 perror("open");
496 return 0;
497 }
498 fp = (knetFile*)calloc(1, sizeof(knetFile));
499 fp->type = KNF_TYPE_LOCAL;
500 fp->fd = fd;
501 fp->ctrl_fd = -1;
502 }
503 if (fp && fp->fd == -1) {
504 knet_close(fp);
505 return 0;
506 }
507 return fp;
508 }
509
knet_dopen(int fd,const char * mode)510 knetFile *knet_dopen(int fd, const char *mode)
511 {
512 knetFile *fp = (knetFile*)calloc(1, sizeof(knetFile));
513 fp->type = KNF_TYPE_LOCAL;
514 fp->fd = fd;
515 return fp;
516 }
517
knet_read(knetFile * fp,void * buf,size_t len)518 ssize_t knet_read(knetFile *fp, void *buf, size_t len)
519 {
520 off_t l = 0;
521 if (fp->fd == -1) return 0;
522 if (fp->type == KNF_TYPE_FTP) {
523 if (fp->is_ready == 0) {
524 if (!fp->no_reconnect) kftp_reconnect(fp);
525 kftp_connect_file(fp);
526 }
527 } else if (fp->type == KNF_TYPE_HTTP) {
528 if (fp->is_ready == 0)
529 khttp_connect_file(fp);
530 }
531 if (fp->type == KNF_TYPE_LOCAL) { // on Windows, the following block is necessary; not on UNIX
532 size_t rest = len;
533 ssize_t curr;
534 while (rest) {
535 do {
536 curr = read(fp->fd, (void*)((char*)buf + l), rest);
537 } while (curr < 0 && EINTR == errno);
538 if (curr < 0) return -1;
539 if (curr == 0) break;
540 l += curr; rest -= curr;
541 }
542 } else l = my_netread(fp->fd, buf, len);
543 fp->offset += l;
544 return l;
545 }
546
knet_seek(knetFile * fp,off_t off,int whence)547 off_t knet_seek(knetFile *fp, off_t off, int whence)
548 {
549 if (whence == SEEK_SET && off == fp->offset) return 0;
550 if (fp->type == KNF_TYPE_LOCAL) {
551 /* Be aware that lseek() returns the offset after seeking, while fseek() returns zero on success. */
552 off_t offset = lseek(fp->fd, off, whence);
553 if (offset == -1) return -1;
554 fp->offset = offset;
555 return fp->offset;
556 } else if (fp->type == KNF_TYPE_FTP) {
557 if (whence == SEEK_CUR) fp->offset += off;
558 else if (whence == SEEK_SET) fp->offset = off;
559 else if (whence == SEEK_END) fp->offset = fp->file_size + off;
560 else return -1;
561 fp->is_ready = 0;
562 return fp->offset;
563 } else if (fp->type == KNF_TYPE_HTTP) {
564 if (whence == SEEK_END) { // FIXME: can we allow SEEK_END in future?
565 fprintf(stderr, "[knet_seek] SEEK_END is not supported for HTTP. Offset is unchanged.\n");
566 errno = ESPIPE;
567 return -1;
568 }
569 if (whence == SEEK_CUR) fp->offset += off;
570 else if (whence == SEEK_SET) fp->offset = off;
571 else return -1;
572 fp->is_ready = 0;
573 return fp->offset;
574 }
575 errno = EINVAL;
576 fprintf(stderr,"[knet_seek] %s\n", strerror(errno));
577 return -1;
578 }
579
knet_close(knetFile * fp)580 int knet_close(knetFile *fp)
581 {
582 if (fp == 0) return 0;
583 if (fp->ctrl_fd != -1) netclose(fp->ctrl_fd); // FTP specific
584 if (fp->fd != -1) {
585 /* On Linux/Mac, netclose() is an alias of close(), but on
586 * Windows, it is an alias of closesocket(). */
587 if (fp->type == KNF_TYPE_LOCAL) close(fp->fd);
588 else netclose(fp->fd);
589 }
590 free(fp->host); free(fp->port);
591 free(fp->response); free(fp->retr); // FTP specific
592 free(fp->path); free(fp->http_host); // HTTP specific
593 free(fp);
594 return 0;
595 }
596
597 #ifdef KNETFILE_MAIN
main(void)598 int main(void)
599 {
600 char *buf;
601 knetFile *fp;
602 int type = 4, l;
603 #ifdef _WIN32
604 knet_win32_init();
605 #endif
606 buf = calloc(0x100000, 1);
607 if (type == 0) {
608 fp = knet_open("knetfile.c", "r");
609 knet_seek(fp, 1000, SEEK_SET);
610 } else if (type == 1) { // NCBI FTP, large file
611 fp = knet_open("ftp://ftp.ncbi.nih.gov/1000genomes/ftp/data/NA12878/alignment/NA12878.chrom6.SLX.SRP000032.2009_06.bam", "r");
612 knet_seek(fp, 2500000000ll, SEEK_SET);
613 l = knet_read(fp, buf, 255);
614 } else if (type == 2) {
615 fp = knet_open("ftp://ftp.sanger.ac.uk/pub4/treefam/tmp/index.shtml", "r");
616 knet_seek(fp, 1000, SEEK_SET);
617 } else if (type == 3) {
618 fp = knet_open("http://www.sanger.ac.uk/Users/lh3/index.shtml", "r");
619 knet_seek(fp, 1000, SEEK_SET);
620 } else if (type == 4) {
621 fp = knet_open("http://www.sanger.ac.uk/Users/lh3/ex1.bam", "r");
622 knet_read(fp, buf, 10000);
623 knet_seek(fp, 20000, SEEK_SET);
624 knet_seek(fp, 10000, SEEK_SET);
625 l = knet_read(fp, buf+10000, 10000000) + 10000;
626 }
627 if (type != 4 && type != 1) {
628 knet_read(fp, buf, 255);
629 buf[255] = 0;
630 printf("%s\n", buf);
631 } else write(fileno(stdout), buf, l);
632 knet_close(fp);
633 free(buf);
634 return 0;
635 }
636 #endif
637