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);
46 BOOL nt_http_post(const char *url, const char *post_data,
47 char *out_buf, size_t out_buf_len,
48 const char *referer, const char *user_agent,
49 nt_link_tp extend_headers,
53 result_code = nt_http_post2(url, post_data,
54 out_buf, out_buf_len, referer, user_agent,
55 extend_headers, cookiep);
56 return (result_code == 200) ? TRUE : FALSE;
59 int nt_http_post2(const char *url, const char *post_data,
60 char *out_buf, size_t out_buf_len,
61 const char *referer, const char *user_agent,
62 nt_link_tp extend_headers,
65 nt_socket_tp socketp = NULL;
66 int fd = -1, sockfd = 0;
68 char ssl_connect[512];
72 nt_http_response_header_tp responsep;
74 nt_link_tp linkp, cookies;
76 char cookie_data[1024];
77 char *cptr, *p1, *p2, *p3;
80 nt_http_header_tp headerp = nt_http_init_header(url);
84 strcpy(data, "POST ");
85 strcat(data, headerp->param);
86 strcat(data, " HTTP/1.1\r\nHost: ");
87 strcat(data, headerp->host);
88 strcat(data, "\r\nAccept: */*");
89 strcat(data, "\r\nAccept-Language: ja");
90 strcat(data, "\r\nUser-Agent: ");
92 strcat(data, user_agent);
94 strcat(data, USER_AGENT);
96 strcat(data, "\r\nReferer: ");
97 strcat(data, referer);
101 cookies = nt_get_cookies(cookiep, headerp->host);
103 cookie_data[0] = '\0';
106 if(cookie_data[0] != '\0')
107 strcat(cookie_data, "; ");
108 strcat(cookie_data, (char*)linkp->data);
110 }while(linkp != cookies);
111 if(strlen(cookie_data) > 0){
112 strcat(data, "\r\nCookie: ");
113 strcat(data, cookie_data);
115 nt_all_link_free(cookies, NULL);
120 linkp = extend_headers;
122 kv = (nt_key_value_tp)linkp->data;
123 strcat(data, "\r\n");
124 strcat(data, kv->key);
126 strcat(data, kv->value);
128 }while(linkp != extend_headers);
130 strcat(data, "\r\nContent-Type: application/x-www-form-urlencoded");
131 strcat(data, "\r\nContent-Length: ");
132 sprintf(itoa_buf, "%u", (unsigned int)strlen(post_data));
133 strcat(data, itoa_buf);
134 strcat(data, "\r\nConnection: close\r\n\r\n");
136 strcat(data, post_data);
139 if(IS_SET_FLAG(headerp,SSL_FLAG)){
140 data_len = strlen(data);
141 sprintf(ssl_connect, "%s:443", headerp->host);
142 if(!nt_ssl_connect(ssl_connect, CA_FILE_PATH, FALSE,
143 data, &data_len, sizeof(data))){
144 nt_http_free_header(headerp);
147 responsep = nt_http_alloc_response_header();
148 if(responsep == NULL){
149 nt_http_free_header(headerp);
152 nread = nt_http_parse_response_header2(data, data_len,
155 nt_http_free_response_header(responsep);
156 nt_http_free_header(headerp);
159 if(out_buf_len <= data_len - nread + 1){
160 nt_http_free_response_header(responsep);
161 nt_http_free_header(headerp);
164 memcpy(out_buf, data+nread, data_len-nread);
165 out_buf[data_len - nread] = '\0';
167 if(headerp->port == -1)
170 socketp = nt_socket_init(headerp->port, headerp->host);
172 nt_http_free_header(headerp);
176 sockfd = nt_socket_connect(socketp, data, strlen(data));
178 nt_http_free_header(headerp);
179 nt_socket_free(socketp);
183 responsep = nt_http_alloc_response_header();
184 if(responsep == NULL){
186 nt_http_free_header(headerp);
187 nt_socket_free(socketp);
191 nread = nt_http_parse_response_header(sockfd,
192 responsep, cookiep, headerp->host);
195 nt_http_free_header(headerp);
196 nt_socket_free(socketp);
197 nt_http_free_response_header(responsep);
201 result = responsep->status_code;
204 if(IS_SET_FLAG(headerp,SSL_FLAG)){
208 nread = read(sockfd, data, sizeof(data));
215 if(out_buf_len <= (cptr - out_buf) + nread){
218 memcpy(cptr, data, nread);
226 }else if(IS_SET_FLAG(responsep, CHUNKED_FLAG)){
228 while(0 < (nread = strtol(p1, &p2, 16))){
229 memmove(p3, p2+2, nread);
246 nt_socket_free(socketp);
247 nt_http_free_response_header(responsep);
248 nt_http_free_header(headerp);
252 BOOL nt_http_get(const char *url, const char *out_path,
253 const char *referer, const char *user_agent,
254 nt_link_tp extend_headers, BOOL range, BOOL ignore_cache)
256 nt_socket_tp socketp;
260 char ssl_connect[512];
264 nt_http_response_header_tp responsep;
270 nt_http_header_tp headerp = nt_http_init_header(url);
274 if(!nt_http_init_header2(headerp, out_path))
278 strcpy(data, "GET ");
279 strcat(data, headerp->param);
280 strcat(data," HTTP/1.1\r\nHost: ");
281 strcat(data, headerp->host);
282 strcat(data, "\r\nUser-Agent: ");
284 strcat(data, user_agent);
286 strcat(data, USER_AGENT);
288 strcat(data, "\r\nReferer: ");
289 strcat(data, referer);
291 if(IS_SET_FLAG(headerp, RANGE_FLAG) && range){
292 if(0 <= sprintf(itoa_buf, "%zu-", headerp->fsize)){
293 strcat(data, "\r\nRange: bytes=");
294 strcat(data, itoa_buf);
297 if(headerp->last_modified){
298 strcat(data, "\r\nIf-Modified-Since: ");
299 strcat(data, headerp->last_modified);
301 if(!IS_SET_FLAG(headerp,SSL_FLAG) &&
302 !IS_SET_FLAG(headerp, RANGE_FLAG))
303 strcat(data, "\r\nAccept-Encoding: gzip");
305 linkp = extend_headers;
307 kv = (nt_key_value_tp)linkp->data;
308 strcat(data, "\r\n");
309 strcat(data, kv->key);
311 strcat(data, kv->value);
313 }while(linkp != extend_headers);
315 strcat(data, "\r\nConnection: close\r\n\r\n");
317 if(IS_SET_FLAG(headerp,SSL_FLAG)){
318 data_len = strlen(data);
319 sprintf(ssl_connect, "%s:443", headerp->host);
320 if(!nt_ssl_connect(ssl_connect, CA_FILE_PATH, FALSE,
321 data, &data_len, sizeof(data))){
322 nt_http_free_header(headerp);
325 /* Not implemented yet. */
329 if(headerp->port == -1)
332 socketp = nt_socket_init(headerp->port, headerp->host);
334 nt_http_free_header(headerp);
338 sockfd = nt_socket_connect(socketp, data, strlen(data));
340 nt_socket_free(socketp);
341 nt_http_free_header(headerp);
345 responsep = nt_http_alloc_response_header();
346 if(responsep == NULL){
348 nt_socket_free(socketp);
349 nt_http_free_header(headerp);
353 nread = nt_http_parse_response_header(sockfd, responsep,
357 nt_socket_free(socketp);
358 nt_http_free_header(headerp);
359 nt_http_free_response_header(responsep);
365 switch(responsep->status_code){
368 O_CREAT | O_WRONLY | O_TRUNC,
369 S_IRUSR | S_IWUSR | S_IROTH);
375 if(IS_SET_FLAG(responsep, GZIP_FLAG)){
376 if(IS_SET_FLAG(responsep, CHUNKED_FLAG)){
377 if(!nt_zip_inflate2(sockfd, fd))
380 if(!nt_zip_inflate(sockfd, fd))
385 nread = read(sockfd, data, sizeof(data));
392 nwrite = write(fd, data, nread);
401 nt_http_save_response_header(out_path, responsep);
404 case 206: /* Partial content */
407 S_IRUSR | S_IWUSR | S_IROTH);
414 nread = read(sockfd, data, sizeof(data));
421 nwrite = write(fd, data, nread);
429 nt_http_save_response_header(out_path, responsep);
432 case 304: /* Not Modified */
445 nt_socket_free(socketp);
446 nt_http_free_header(headerp);
447 nt_http_free_response_header(responsep);
449 return nt_http_get(url, out_path, referer, user_agent,
450 extend_headers, FALSE, ignore_cache);
456 static BOOL nt_http_init_header2(
457 nt_http_header_tp headerp, const char *out_path)
459 char key[DB_KEY_SIZE_MAX];
460 nt_db_log_rec_tp recp;
462 const char *log_path;
465 nt_mutex_handle h_mutex;
470 if(!nt_db_cpy_key(out_path, key)){
474 h_mutex = nt_mutex_get_one_time_handle(NT_MUTEX_ROOT_NAME_FILE);
478 if(!nt_mutex_add_moniker(h_mutex, LOG_DB_W_NAME)){
481 if(!nt_mutex_lock(h_mutex)){
484 log_path = nt_db_get_log_path();
485 dbm = dbm_open((char*)log_path, O_RDWR | O_CREAT, 0600);
487 nt_mutex_unlock(h_mutex);
491 d_key.dsize = strlen(key);
492 d_key.dptr = (void*)key;
494 d_data = dbm_fetch(dbm, d_key);
495 if(d_data.dptr && d_data.dsize == sizeof(nt_db_log_rec_t)){
496 recp = (nt_db_log_rec_tp)d_data.dptr;
497 headerp->last_modified = nt_trim(recp->last_modified);
499 headerp->last_modified = NULL;
502 nt_mutex_unlock(h_mutex);
504 if(0 == stat(out_path, &st)){
505 headerp->fsize = st.st_size;
506 SET_FLAG(headerp, RANGE_FLAG);
515 static nt_http_header_tp nt_http_init_header(const char *url)
517 nt_http_header_tp headerp;
518 char *cptr, *host, *param;
523 if(0 == strncmp(url, "http://", 7)){
526 }else if(0 == strncmp(url, "https://", 8)){
534 cptr = strchr(url, '/');
542 host = malloc(strlen(url)+1);
548 param = malloc(strlen(cptr)+1);
554 host = malloc(len+1);
558 memcpy(host, url, len);
563 if(nt_is_ipv6_addr(host, &cptr)){
567 memmove(host, host+1, cptr - host);
573 port = (port == 0) ? -1 : port;
577 port = (port == 0) ? -1 : port;
579 cptr = strchr(host, ':');
580 if(cptr != NULL && strlen(cptr+1) > 0){
582 port = (port == 0) ? -1 : port;
587 cptr = strrchr(host, ':');
588 if(cptr != NULL && strlen(cptr) > 0){
590 port = (port == 0) ? -1 : port;
594 headerp = calloc(1, sizeof(nt_http_header_t));
601 headerp->port = port;
602 headerp->host = host;
603 headerp->param = param;
605 SET_FLAG(headerp, SSL_FLAG);
607 CLR_FLAG(headerp, SSL_FLAG);
612 static void nt_http_free_header(nt_http_header_tp headerp)
614 if(headerp->host != NULL)
616 if(headerp->param != NULL)
617 free(headerp->param);
618 if(headerp->last_modified != NULL)
619 free(headerp->last_modified);