1 /* Copyright 2013 Akira Ohta (akohta001@gmail.com)
2 This file is part of ntch.
4 The ntch is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 The ntch is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with ntch. If not, see <http://www.gnu.org/licenses/>.
18 #include <sys/types.h>
19 #include <sys/socket.h>
21 #include <openssl/sha.h>
23 #include <arpa/inet.h>
31 #include "utils/nt_std_t.h"
32 #include "utils/text.h"
33 #include "utils/zip.h"
35 #include "utils/nt_mutex.h"
36 #include "net/nt_socket.h"
37 #include "net/nt_http.h"
38 #include "net/nt_cookie.h"
41 static nt_http_header_tp nt_http_init_header(const char *url);
42 static void nt_http_free_header(nt_http_header_tp headerp);
43 static BOOL nt_http_init_header2(
44 nt_http_header_tp headerp, const char *out_path);
45 static int nt_http_post_local(const char *url, const char *post_data,
46 char **out_buf, size_t out_buf_len,
47 const char *referer, const char *user_agent,
48 nt_link_tp extend_headers,
49 nt_cookie_tp cookiep);
51 BOOL nt_http_post(const char *url, const char *post_data,
52 char *out_buf, size_t out_buf_len,
53 const char *referer, const char *user_agent,
54 nt_link_tp extend_headers,
58 result_code = nt_http_post_local(url, post_data,
59 &out_buf, out_buf_len, referer, user_agent,
60 extend_headers, cookiep);
61 return (result_code == 200) ? TRUE : FALSE;
64 int nt_http_post2(const char *url, const char *post_data,
65 char *out_buf, size_t out_buf_len,
66 const char *referer, const char *user_agent,
67 nt_link_tp extend_headers,
70 return nt_http_post_local(url, post_data,
71 &out_buf, out_buf_len,
76 int nt_http_post3(const char *url, const char *post_data,
78 const char *referer, const char *user_agent,
79 nt_link_tp extend_headers,
82 return nt_http_post_local(url, post_data,
89 #define BUF_LEN_DELTA (1024*4)
90 int nt_http_post_local(const char *url, const char *post_data,
91 char **out_buf, size_t out_buf_len,
92 const char *referer, const char *user_agent,
93 nt_link_tp extend_headers,
96 nt_socket_tp socketp = NULL;
97 int fd = -1, sockfd = 0;
99 char ssl_connect[512];
103 nt_http_response_header_tp responsep;
105 nt_link_tp linkp, cookies;
107 char cookie_data[1024];
108 char *cptr, *p1, *p2, *p3;
115 nt_http_header_tp headerp = nt_http_init_header(url);
119 strcpy(data, "POST ");
120 strcat(data, headerp->param);
121 strcat(data, " HTTP/1.1\r\nHost: ");
122 strcat(data, headerp->host);
123 strcat(data, "\r\nAccept: */*");
124 strcat(data, "\r\nAccept-Language: ja");
125 strcat(data, "\r\nUser-Agent: ");
127 strcat(data, user_agent);
129 strcat(data, USER_AGENT);
131 strcat(data, "\r\nReferer: ");
132 strcat(data, referer);
136 cookies = nt_get_cookies(cookiep, headerp->host);
138 cookie_data[0] = '\0';
141 if(cookie_data[0] != '\0')
142 strcat(cookie_data, "; ");
143 strcat(cookie_data, (char*)linkp->data);
145 }while(linkp != cookies);
146 if(strlen(cookie_data) > 0){
147 strcat(data, "\r\nCookie: ");
148 strcat(data, cookie_data);
150 nt_all_link_free(cookies, NULL);
155 linkp = extend_headers;
157 kv = (nt_key_value_tp)linkp->data;
158 strcat(data, "\r\n");
159 strcat(data, kv->key);
161 strcat(data, kv->value);
163 }while(linkp != extend_headers);
165 strcat(data, "\r\nContent-Type: application/x-www-form-urlencoded");
166 strcat(data, "\r\nContent-Length: ");
167 sprintf(itoa_buf, "%u", (unsigned int)strlen(post_data));
168 strcat(data, itoa_buf);
169 strcat(data, "\r\nConnection: close\r\n\r\n");
171 strcat(data, post_data);
174 if(IS_SET_FLAG(headerp,SSL_FLAG)){
175 data_len = strlen(data);
176 sprintf(ssl_connect, "%s:443", headerp->host);
177 if(!nt_ssl_connect(ssl_connect, CA_FILE_PATH, FALSE,
178 data, &data_len, sizeof(data))){
179 nt_http_free_header(headerp);
182 responsep = nt_http_alloc_response_header();
183 if(responsep == NULL){
184 nt_http_free_header(headerp);
187 nread = nt_http_parse_response_header2(data, data_len,
190 nt_http_free_response_header(responsep);
191 nt_http_free_header(headerp);
194 if(out_buf_len <= data_len - nread + 1){
195 nt_http_free_response_header(responsep);
196 nt_http_free_header(headerp);
199 memcpy(*out_buf, data+nread, data_len-nread);
200 (*out_buf)[data_len - nread] = '\0';
202 if(headerp->port == -1)
205 socketp = nt_socket_init(headerp->port, headerp->host);
207 nt_http_free_header(headerp);
211 sockfd = nt_socket_connect(socketp, data, strlen(data));
213 nt_http_free_header(headerp);
214 nt_socket_free(socketp);
218 responsep = nt_http_alloc_response_header();
219 if(responsep == NULL){
221 nt_http_free_header(headerp);
222 nt_socket_free(socketp);
226 nread = nt_http_parse_response_header(sockfd,
227 responsep, cookiep, headerp->host);
230 nt_http_free_header(headerp);
231 nt_socket_free(socketp);
232 nt_http_free_response_header(responsep);
238 result = responsep->status_code;
241 if(IS_SET_FLAG(headerp,SSL_FLAG)){
243 if(out_buf_len == 0){
244 tmp_buf = malloc(BUF_LEN_DELTA);
248 out_buf_len = BUF_LEN_DELTA;
249 is_realloc_buf = TRUE;
251 is_realloc_buf = FALSE;
255 nread = read(sockfd, data, sizeof(data));
262 len = cptr - (*out_buf);
263 if(out_buf_len <= len + nread){
266 new_len = ((len + nread) / BUF_LEN_DELTA + 1) * BUF_LEN_DELTA;
267 tmp_buf = malloc(new_len);
270 memcpy(tmp_buf, *out_buf, len);
273 out_buf_len = new_len;
274 cptr = (*out_buf) + len;
276 memcpy(cptr, data, nread);
284 }else if(IS_SET_FLAG(responsep, CHUNKED_FLAG)){
286 while(0 < (nread = strtol(p1, &p2, 16))){
287 memmove(p3, p2+2, nread);
304 nt_socket_free(socketp);
305 nt_http_free_response_header(responsep);
306 nt_http_free_header(headerp);
310 BOOL nt_http_get(const char *url, const char *out_path,
311 const char *referer, const char *user_agent,
312 nt_link_tp extend_headers, BOOL range, BOOL ignore_cache)
314 nt_socket_tp socketp;
318 char ssl_connect[512];
322 nt_http_response_header_tp responsep;
328 nt_http_header_tp headerp = nt_http_init_header(url);
332 if(!nt_http_init_header2(headerp, out_path))
336 strcpy(data, "GET ");
337 strcat(data, headerp->param);
338 strcat(data," HTTP/1.1\r\nHost: ");
339 strcat(data, headerp->host);
340 strcat(data, "\r\nUser-Agent: ");
342 strcat(data, user_agent);
344 strcat(data, USER_AGENT);
346 strcat(data, "\r\nReferer: ");
347 strcat(data, referer);
349 if(IS_SET_FLAG(headerp, RANGE_FLAG) && range){
350 if(0 <= sprintf(itoa_buf, "%zu-", headerp->fsize)){
351 strcat(data, "\r\nRange: bytes=");
352 strcat(data, itoa_buf);
355 if(headerp->last_modified){
356 strcat(data, "\r\nIf-Modified-Since: ");
357 strcat(data, headerp->last_modified);
359 if(!IS_SET_FLAG(headerp,SSL_FLAG) &&
360 !IS_SET_FLAG(headerp, RANGE_FLAG))
361 strcat(data, "\r\nAccept-Encoding: gzip");
363 linkp = extend_headers;
365 kv = (nt_key_value_tp)linkp->data;
366 strcat(data, "\r\n");
367 strcat(data, kv->key);
369 strcat(data, kv->value);
371 }while(linkp != extend_headers);
373 strcat(data, "\r\nConnection: close\r\n\r\n");
375 if(IS_SET_FLAG(headerp,SSL_FLAG)){
376 data_len = strlen(data);
377 sprintf(ssl_connect, "%s:443", headerp->host);
378 if(!nt_ssl_connect(ssl_connect, CA_FILE_PATH, FALSE,
379 data, &data_len, sizeof(data))){
380 nt_http_free_header(headerp);
383 /* Not implemented yet. */
387 if(headerp->port == -1)
390 socketp = nt_socket_init(headerp->port, headerp->host);
392 nt_http_free_header(headerp);
396 sockfd = nt_socket_connect(socketp, data, strlen(data));
398 nt_socket_free(socketp);
399 nt_http_free_header(headerp);
403 responsep = nt_http_alloc_response_header();
404 if(responsep == NULL){
406 nt_socket_free(socketp);
407 nt_http_free_header(headerp);
411 nread = nt_http_parse_response_header(sockfd, responsep,
415 nt_socket_free(socketp);
416 nt_http_free_header(headerp);
417 nt_http_free_response_header(responsep);
423 switch(responsep->status_code){
426 O_CREAT | O_WRONLY | O_TRUNC,
427 S_IRUSR | S_IWUSR | S_IROTH);
433 if(IS_SET_FLAG(responsep, GZIP_FLAG)){
434 if(IS_SET_FLAG(responsep, CHUNKED_FLAG)){
435 if(!nt_zip_inflate2(sockfd, fd))
438 if(!nt_zip_inflate(sockfd, fd))
443 nread = read(sockfd, data, sizeof(data));
450 nwrite = write(fd, data, nread);
459 nt_http_save_response_header(out_path, responsep);
462 case 206: /* Partial content */
465 S_IRUSR | S_IWUSR | S_IROTH);
472 nread = read(sockfd, data, sizeof(data));
479 nwrite = write(fd, data, nread);
487 nt_http_save_response_header(out_path, responsep);
490 case 304: /* Not Modified */
503 nt_socket_free(socketp);
504 nt_http_free_header(headerp);
505 nt_http_free_response_header(responsep);
507 return nt_http_get(url, out_path, referer, user_agent,
508 extend_headers, FALSE, ignore_cache);
514 static BOOL nt_http_init_header2(
515 nt_http_header_tp headerp, const char *out_path)
517 char key[DB_KEY_SIZE_MAX];
518 nt_db_log_rec_tp recp;
520 const char *log_path;
523 nt_mutex_handle h_mutex;
528 if(!nt_db_cpy_key(out_path, key)){
532 h_mutex = nt_mutex_get_one_time_handle(NT_MUTEX_ROOT_NAME_FILE);
536 if(!nt_mutex_add_moniker(h_mutex, LOG_DB_W_NAME)){
539 if(!nt_mutex_lock(h_mutex)){
542 log_path = nt_db_get_log_path();
543 dbm = dbm_open((char*)log_path, O_RDWR | O_CREAT, 0600);
545 nt_mutex_unlock(h_mutex);
549 d_key.dsize = strlen(key);
550 d_key.dptr = (void*)key;
552 d_data = dbm_fetch(dbm, d_key);
553 if(d_data.dptr && d_data.dsize == sizeof(nt_db_log_rec_t)){
554 recp = (nt_db_log_rec_tp)d_data.dptr;
555 headerp->last_modified = nt_trim(recp->last_modified);
557 headerp->last_modified = NULL;
560 nt_mutex_unlock(h_mutex);
562 if(0 == stat(out_path, &st)){
563 headerp->fsize = st.st_size;
564 SET_FLAG(headerp, RANGE_FLAG);
573 static nt_http_header_tp nt_http_init_header(const char *url)
575 nt_http_header_tp headerp;
576 char *cptr, *host, *param;
581 if(0 == strncmp(url, "http://", 7)){
584 }else if(0 == strncmp(url, "https://", 8)){
592 cptr = strchr(url, '/');
600 host = malloc(strlen(url)+1);
606 param = malloc(strlen(cptr)+1);
612 host = malloc(len+1);
616 memcpy(host, url, len);
621 if(nt_is_ipv6_addr(host, &cptr)){
625 memmove(host, host+1, cptr - host);
631 port = (port == 0) ? -1 : port;
635 port = (port == 0) ? -1 : port;
637 cptr = strchr(host, ':');
638 if(cptr != NULL && strlen(cptr+1) > 0){
640 port = (port == 0) ? -1 : port;
645 cptr = strrchr(host, ':');
646 if(cptr != NULL && strlen(cptr) > 0){
648 port = (port == 0) ? -1 : port;
652 headerp = calloc(1, sizeof(nt_http_header_t));
659 headerp->port = port;
660 headerp->host = host;
661 headerp->param = param;
663 SET_FLAG(headerp, SSL_FLAG);
665 CLR_FLAG(headerp, SSL_FLAG);
670 static void nt_http_free_header(nt_http_header_tp headerp)
672 if(headerp->host != NULL)
674 if(headerp->param != NULL)
675 free(headerp->param);
676 if(headerp->last_modified != NULL)
677 free(headerp->last_modified);