kopen.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. #include <stdio.h>
  2. #include <fcntl.h>
  3. #include <errno.h>
  4. #include <ctype.h>
  5. #include <unistd.h>
  6. #include <string.h>
  7. #include <stdlib.h>
  8. #include <signal.h>
  9. #include <sys/types.h>
  10. #ifndef _WIN32
  11. #include <netdb.h>
  12. #include <arpa/inet.h>
  13. #include <sys/socket.h>
  14. #endif
  15. #ifdef _WIN32
  16. #define _KO_NO_NET
  17. #endif
  18. #ifndef _KO_NO_NET
  19. static int socket_wait(int fd, int is_read)
  20. {
  21. fd_set fds, *fdr = 0, *fdw = 0;
  22. struct timeval tv;
  23. int ret;
  24. tv.tv_sec = 5; tv.tv_usec = 0; // 5 seconds time out
  25. FD_ZERO(&fds);
  26. FD_SET(fd, &fds);
  27. if (is_read) fdr = &fds;
  28. else fdw = &fds;
  29. ret = select(fd+1, fdr, fdw, 0, &tv);
  30. if (ret == -1) perror("select");
  31. return ret;
  32. }
  33. static int socket_connect(const char *host, const char *port)
  34. {
  35. #define __err_connect(func) do { perror(func); freeaddrinfo(res); return -1; } while (0)
  36. int on = 1, fd;
  37. struct linger lng = { 0, 0 };
  38. struct addrinfo hints, *res = 0;
  39. memset(&hints, 0, sizeof(struct addrinfo));
  40. hints.ai_family = AF_UNSPEC;
  41. hints.ai_socktype = SOCK_STREAM;
  42. if (getaddrinfo(host, port, &hints, &res) != 0) __err_connect("getaddrinfo");
  43. if ((fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) __err_connect("socket");
  44. if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) __err_connect("setsockopt");
  45. if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &lng, sizeof(lng)) == -1) __err_connect("setsockopt");
  46. if (connect(fd, res->ai_addr, res->ai_addrlen) != 0) __err_connect("connect");
  47. freeaddrinfo(res);
  48. return fd;
  49. #undef __err_connect
  50. }
  51. static int http_open(const char *fn)
  52. {
  53. char *p, *proxy, *q, *http_host, *host, *port, *path, *buf;
  54. int fd, ret, l;
  55. /* parse URL; adapted from khttp_parse_url() in knetfile.c */
  56. if (strstr(fn, "http://") != fn) return 0;
  57. // set ->http_host
  58. for (p = (char*)fn + 7; *p && *p != '/'; ++p);
  59. l = p - fn - 7;
  60. http_host = calloc(l + 1, 1);
  61. strncpy(http_host, fn + 7, l);
  62. http_host[l] = 0;
  63. for (q = http_host; *q && *q != ':'; ++q);
  64. if (*q == ':') *q++ = 0;
  65. // get http_proxy
  66. proxy = getenv("http_proxy");
  67. // set host, port and path
  68. if (proxy == 0) {
  69. host = strdup(http_host); // when there is no proxy, server name is identical to http_host name.
  70. port = strdup(*q? q : "80");
  71. path = strdup(*p? p : "/");
  72. } else {
  73. host = (strstr(proxy, "http://") == proxy)? strdup(proxy + 7) : strdup(proxy);
  74. for (q = host; *q && *q != ':'; ++q);
  75. if (*q == ':') *q++ = 0;
  76. port = strdup(*q? q : "80");
  77. path = strdup(fn);
  78. }
  79. /* connect; adapted from khttp_connect() in knetfile.c */
  80. l = 0;
  81. fd = socket_connect(host, port);
  82. buf = calloc(0x10000, 1); // FIXME: I am lazy... But in principle, 64KB should be large enough.
  83. l += sprintf(buf + l, "GET %s HTTP/1.0\r\nHost: %s\r\n", path, http_host);
  84. l += sprintf(buf + l, "\r\n");
  85. write(fd, buf, l);
  86. l = 0;
  87. while (read(fd, buf + l, 1)) { // read HTTP header; FIXME: bad efficiency
  88. if (buf[l] == '\n' && l >= 3)
  89. if (strncmp(buf + l - 3, "\r\n\r\n", 4) == 0) break;
  90. ++l;
  91. }
  92. buf[l] = 0;
  93. if (l < 14) { // prematured header
  94. close(fd);
  95. fd = -1;
  96. }
  97. ret = strtol(buf + 8, &p, 0); // HTTP return code
  98. if (ret != 200) {
  99. close(fd);
  100. fd = -1;
  101. }
  102. free(buf); free(http_host); free(host); free(port); free(path);
  103. return fd;
  104. }
  105. typedef struct {
  106. int max_response, ctrl_fd;
  107. char *response;
  108. } ftpaux_t;
  109. static int kftp_get_response(ftpaux_t *aux)
  110. {
  111. unsigned char c;
  112. int n = 0;
  113. char *p;
  114. if (socket_wait(aux->ctrl_fd, 1) <= 0) return 0;
  115. while (read(aux->ctrl_fd, &c, 1)) { // FIXME: this is *VERY BAD* for unbuffered I/O
  116. if (n >= aux->max_response) {
  117. aux->max_response = aux->max_response? aux->max_response<<1 : 256;
  118. aux->response = realloc(aux->response, aux->max_response);
  119. }
  120. aux->response[n++] = c;
  121. if (c == '\n') {
  122. if (n >= 4 && isdigit(aux->response[0]) && isdigit(aux->response[1]) && isdigit(aux->response[2])
  123. && aux->response[3] != '-') break;
  124. n = 0;
  125. continue;
  126. }
  127. }
  128. if (n < 2) return -1;
  129. aux->response[n-2] = 0;
  130. return strtol(aux->response, &p, 0);
  131. }
  132. static int kftp_send_cmd(ftpaux_t *aux, const char *cmd, int is_get)
  133. {
  134. if (socket_wait(aux->ctrl_fd, 0) <= 0) return -1; // socket is not ready for writing
  135. write(aux->ctrl_fd, cmd, strlen(cmd));
  136. return is_get? kftp_get_response(aux) : 0;
  137. }
  138. static int ftp_open(const char *fn)
  139. {
  140. char *p, *host = 0, *port = 0, *retr = 0;
  141. char host2[80], port2[10];
  142. int v[6], l, fd = -1, ret, pasv_port, pasv_ip[4];
  143. ftpaux_t aux;
  144. /* parse URL */
  145. if (strstr(fn, "ftp://") != fn) return 0;
  146. for (p = (char*)fn + 6; *p && *p != '/'; ++p);
  147. if (*p != '/') return 0;
  148. l = p - fn - 6;
  149. port = strdup("21");
  150. host = calloc(l + 1, 1);
  151. strncpy(host, fn + 6, l);
  152. retr = calloc(strlen(p) + 8, 1);
  153. sprintf(retr, "RETR %s\r\n", p);
  154. /* connect to ctrl */
  155. memset(&aux, 0, sizeof(ftpaux_t));
  156. aux.ctrl_fd = socket_connect(host, port);
  157. if (aux.ctrl_fd == -1) goto ftp_open_end; /* fail to connect ctrl */
  158. /* connect to the data stream */
  159. kftp_get_response(&aux);
  160. kftp_send_cmd(&aux, "USER anonymous\r\n", 1);
  161. kftp_send_cmd(&aux, "PASS kopen@\r\n", 1);
  162. kftp_send_cmd(&aux, "TYPE I\r\n", 1);
  163. kftp_send_cmd(&aux, "PASV\r\n", 1);
  164. for (p = aux.response; *p && *p != '('; ++p);
  165. if (*p != '(') goto ftp_open_end;
  166. ++p;
  167. sscanf(p, "%d,%d,%d,%d,%d,%d", &v[0], &v[1], &v[2], &v[3], &v[4], &v[5]);
  168. memcpy(pasv_ip, v, 4 * sizeof(int));
  169. pasv_port = (v[4]<<8&0xff00) + v[5];
  170. kftp_send_cmd(&aux, retr, 0);
  171. sprintf(host2, "%d.%d.%d.%d", pasv_ip[0], pasv_ip[1], pasv_ip[2], pasv_ip[3]);
  172. sprintf(port2, "%d", pasv_port);
  173. fd = socket_connect(host2, port2);
  174. if (fd == -1) goto ftp_open_end;
  175. ret = kftp_get_response(&aux);
  176. if (ret != 150) {
  177. close(fd);
  178. fd = -1;
  179. }
  180. close(aux.ctrl_fd);
  181. ftp_open_end:
  182. free(host); free(port); free(retr); free(aux.response);
  183. return fd;
  184. }
  185. #endif /* !defined(_KO_NO_NET) */
  186. static char **cmd2argv(const char *cmd)
  187. {
  188. int i, beg, end, argc;
  189. char **argv, *p, *q, *str;
  190. end = strlen(cmd);
  191. for (i = end - 1; i >= 0; --i)
  192. if (!isspace(cmd[i])) break;
  193. end = i + 1;
  194. for (beg = 0; beg < end; ++beg)
  195. if (!isspace(cmd[beg])) break;
  196. if (beg == end) return 0;
  197. for (i = beg + 1, argc = 0; i < end; ++i)
  198. if (isspace(cmd[i]) && !isspace(cmd[i-1]))
  199. ++argc;
  200. argv = (char**)calloc(argc + 2, sizeof(void*));
  201. argv[0] = str = (char*)calloc(end - beg + 1, 1);
  202. strncpy(argv[0], cmd + beg, end - beg);
  203. for (i = argc = 1, q = p = str; i < end - beg; ++i)
  204. if (isspace(str[i])) str[i] = 0;
  205. else if (str[i] && str[i-1] == 0) argv[argc++] = &str[i];
  206. return argv;
  207. }
  208. #define KO_STDIN 1
  209. #define KO_FILE 2
  210. #define KO_PIPE 3
  211. #define KO_HTTP 4
  212. #define KO_FTP 5
  213. typedef struct {
  214. int type, fd;
  215. pid_t pid;
  216. } koaux_t;
  217. void *kopen(const char *fn, int *_fd)
  218. {
  219. koaux_t *aux = 0;
  220. *_fd = -1;
  221. if (strstr(fn, "http://") == fn) {
  222. aux = calloc(1, sizeof(koaux_t));
  223. aux->type = KO_HTTP;
  224. aux->fd = http_open(fn);
  225. } else if (strstr(fn, "ftp://") == fn) {
  226. aux = calloc(1, sizeof(koaux_t));
  227. aux->type = KO_FTP;
  228. aux->fd = ftp_open(fn);
  229. } else if (strcmp(fn, "-") == 0) {
  230. aux = calloc(1, sizeof(koaux_t));
  231. aux->type = KO_STDIN;
  232. aux->fd = STDIN_FILENO;
  233. } else {
  234. const char *p, *q;
  235. for (p = fn; *p; ++p)
  236. if (!isspace(*p)) break;
  237. if (*p == '<') { // pipe open
  238. int need_shell, pfd[2];
  239. pid_t pid;
  240. // a simple check to see if we need to invoke a shell; not always working
  241. for (q = p + 1; *q; ++q)
  242. if (ispunct(*q) && *q != '.' && *q != '_' && *q != '-' && *q != ':')
  243. break;
  244. need_shell = (*q != 0);
  245. pipe(pfd);
  246. pid = vfork();
  247. if (pid == -1) { /* vfork() error */
  248. close(pfd[0]); close(pfd[1]);
  249. return 0;
  250. }
  251. if (pid == 0) { /* the child process */
  252. char **argv; /* FIXME: I do not know if this will lead to a memory leak */
  253. close(pfd[0]);
  254. dup2(pfd[1], STDOUT_FILENO);
  255. close(pfd[1]);
  256. if (!need_shell) {
  257. argv = cmd2argv(p + 1);
  258. execvp(argv[0], argv);
  259. free(argv[0]); free(argv);
  260. } else execl("/bin/sh", "sh", "-c", p + 1, NULL);
  261. exit(1);
  262. } else { /* parent process */
  263. close(pfd[1]);
  264. aux = calloc(1, sizeof(koaux_t));
  265. aux->type = KO_PIPE;
  266. aux->fd = pfd[0];
  267. aux->pid = pid;
  268. }
  269. } else {
  270. #ifdef _WIN32
  271. *_fd = open(fn, O_RDONLY | O_BINARY);
  272. #else
  273. *_fd = open(fn, O_RDONLY);
  274. #endif
  275. if (*_fd) {
  276. aux = calloc(1, sizeof(koaux_t));
  277. aux->type = KO_FILE;
  278. aux->fd = *_fd;
  279. }
  280. }
  281. }
  282. *_fd = aux->fd;
  283. return aux;
  284. }
  285. int kclose(void *a)
  286. {
  287. koaux_t *aux = (koaux_t*)a;
  288. if (aux->type == KO_PIPE) {
  289. int status;
  290. pid_t pid;
  291. pid = waitpid(aux->pid, &status, WNOHANG);
  292. if (pid != aux->pid) kill(aux->pid, 15);
  293. }
  294. return 0;
  295. }
  296. #ifdef _KO_MAIN
  297. #define BUF_SIZE 0x10000
  298. int main(int argc, char *argv[])
  299. {
  300. void *x;
  301. int l, fd;
  302. unsigned char buf[BUF_SIZE];
  303. FILE *fp;
  304. if (argc == 1) {
  305. fprintf(stderr, "Usage: kopen <file>\n");
  306. return 1;
  307. }
  308. x = kopen(argv[1], &fd);
  309. fp = fdopen(fd, "r");
  310. if (fp == 0) {
  311. fprintf(stderr, "ERROR: fail to open the input\n");
  312. return 1;
  313. }
  314. do {
  315. if ((l = fread(buf, 1, BUF_SIZE, fp)) != 0)
  316. fwrite(buf, 1, l, stdout);
  317. } while (l == BUF_SIZE);
  318. fclose(fp);
  319. kclose(x);
  320. return 0;
  321. }
  322. #endif