// hogehoge/abc/ddd/// -> hogehoge/abc/ char *get_parent_path(char *dst, char *src, size_t len) { char *p; if (strlen(src) > len || len <= 0) { debug_log_output("get_parent_path: no enough space"); strncpy(dst, "/", len); return dst; } // とりあえず全部コピー。 strncpy(dst, src, len); // 一番後ろの '/' を削除 for (p = dst + strlen(dst) - 1; p > dst && *p == '/'; p--) *p = '\0'; p = strrchr(dst, '/'); if (p == NULL || p == dst) { debug_log_output("get_parent_path: no / anymore..., i.e., root."); strncpy(dst, "/", len); return dst; } // 残った最後の '/' より後ろを削除 *++p = '\0'; return dst; }
// ************************************************************************** // accept_socketから、1行(CRLFか、LF単独が現れるまで)受信 // CRLFは削除する。 // 受信したサイズをreturnする。 // ************************************************************************** int line_receive(int accept_socket, char *line_buf_p, int line_max) { char byte_buf; int line_len=0; int recv_len; // 1行受信実行 while ( 1 ){ recv_len = recv(accept_socket, &byte_buf, 1, 0); if ( recv_len != 1 ){ // 受信失敗チェック #ifdef linux debug_log_output("header read error cnt = %d error=%s\n", recv_len, strerror(errno)); #else debug_log_output("header read error cnt = %d socket=%d error=%d\n", recv_len, accept_socket, WSAGetLastError()); #endif return ( -1 ); } // CR/LFチェック if ( byte_buf == '\r' ){ continue; }else if ( byte_buf == '\n' ){ *line_buf_p = 0; break; } // バッファにセット *line_buf_p++ = byte_buf; // 受信バッファサイズチェック if ( ++line_len >= line_max){ // バッファオーバーフロー検知 debug_log_output("line_buf over flow."); return ( -1 ); } } return line_len; }
FILETYPES http_index( HTTP_RECV_INFO* http_recv_info_p ) { char document_path[FILENAME_MAX]; char read_filename[FILENAME_MAX]; char file_extension[16]; //Path Normalize. strncpy(document_path, http_recv_info_p->send_filename, sizeof(document_path) ); if ( document_path[ strlen(document_path)-1 ] != DELIMITER[0] ){// 最後が'/'じゃなかったら、'/'を追加 strncat(document_path, DELIMITER, sizeof(document_path) -strlen(document_path) ); } // ---------------------------------------------- // document_root/index.* のフルパス生成 // ---------------------------------------------- snprintf(read_filename, sizeof( read_filename),"%sindex.html",document_path ); if( access( read_filename , 0 ) == 0 ){ strcat(http_recv_info_p->request_uri,"index.html"); strcpy(http_recv_info_p->send_filename,read_filename); // ファイルの拡張子より、Content-type を決定 filename_to_extension(http_recv_info_p->send_filename, file_extension, sizeof(file_extension)); debug_log_output("http_recv_info_p->send_filename='%s', file_extension='%s'\n", http_recv_info_p->send_filename, file_extension); // 拡張子から、mime_typeを導く。 check_file_extension_to_mime_type(file_extension, http_recv_info_p->mime_type, sizeof(http_recv_info_p->mime_type)); return _FILE; } snprintf(read_filename, sizeof( read_filename),"%sindex.htm",document_path ); if( access( read_filename , 0 ) == 0 ){ strcat(http_recv_info_p->request_uri,"index.htm"); strcpy(http_recv_info_p->send_filename,read_filename); // ファイルの拡張子より、Content-type を決定 filename_to_extension(http_recv_info_p->send_filename, file_extension, sizeof(file_extension)); debug_log_output("http_recv_info_p->send_filename='%s', file_extension='%s'\n", http_recv_info_p->send_filename, file_extension); // 拡張子から、mime_typeを導く。 check_file_extension_to_mime_type(file_extension, http_recv_info_p->mime_type, sizeof(http_recv_info_p->mime_type)); return _FILE; } snprintf(read_filename, sizeof( read_filename),"%sindex.php",document_path ); if( access( read_filename , 0 ) == 0 ){ strcat(http_recv_info_p->request_uri,"index.php"); strcpy(http_recv_info_p->send_filename,read_filename); // ファイルの拡張子より、Content-type を決定 filename_to_extension(http_recv_info_p->send_filename, file_extension, sizeof(file_extension)); debug_log_output("http_recv_info_p->send_filename='%s', file_extension='%s'\n", http_recv_info_p->send_filename, file_extension); // 拡張子から、mime_typeを導く。 check_file_extension_to_mime_type(file_extension, http_recv_info_p->mime_type, sizeof(http_recv_info_p->mime_type)); return _CGI; } snprintf(read_filename, sizeof( read_filename),"%sindex.jss",document_path ); if( access( read_filename , 0 ) == 0 ){ strcat(http_recv_info_p->request_uri,"index.jss"); strcpy(http_recv_info_p->send_filename,read_filename); // ファイルの拡張子より、Content-type を決定 filename_to_extension(http_recv_info_p->send_filename, file_extension, sizeof(file_extension)); debug_log_output("http_recv_info_p->send_filename='%s', file_extension='%s'\n", http_recv_info_p->send_filename, file_extension); // 拡張子から、mime_typeを導く。 check_file_extension_to_mime_type(file_extension, http_recv_info_p->mime_type, sizeof(http_recv_info_p->mime_type)); return _CGI; } return _DIR; }
static int http_not_found_response(int accept_socket, HTTP_RECV_INFO *http_recv_info_p) { char buffer[WIZD_FILENAME_MAX]; if((strlen(http_recv_info_p->mime_type)==0) || (strcmp(http_recv_info_p->mime_type, "text/html") == 0)) { snprintf(buffer, sizeof(buffer), HTTP_NOT_FOUND HTTP_CONNECTION HTTP_CONTENT_TYPE HTTP_END "<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>\r\n" "<BODY>File not found<P>\r\n" "%s\r\n" "</BODY></HTML>\r\n", http_recv_info_p->mime_type, http_recv_info_p->recv_uri ); } else { snprintf(buffer, sizeof(buffer), HTTP_NOT_FOUND HTTP_CONNECTION HTTP_CONTENT_TYPE HTTP_END, "text/plain" ); } debug_log_output("Not found: %s\n%s", http_recv_info_p->recv_uri, buffer); write(accept_socket, buffer, strlen(buffer)+1); return 0; /* return http_redirect_response(accept_socket, http_recv_info_p, "/"); */ }
static int http_redirect_response(int accept_socket, HTTP_RECV_INFO *http_recv_info, char *location) { IGNORE_PARAMETER(http_recv_info); char buffer[FILENAME_MAX]; snprintf(buffer, sizeof(buffer),"HTTP/1.1 301 Found\r\n" "Location: %s\r\n" "\r\n", location ); send( accept_socket , buffer , strlen( buffer ) + 1 , 0 ); debug_log_output("Redirect to %s",location); return 0; }
void set_blocking_mode(int fd, int flag) { int res, nonb = 0; nonb |= O_NONBLOCK; if ((res = fcntl(fd, F_GETFL, 0)) == -1) { debug_log_output("fcntl(fd, F_GETFL) failed"); } if (flag) { res |= O_NONBLOCK; } else { res &= ~O_NONBLOCK; } if (fcntl(fd, F_SETFL, res) == -1) { debug_log_output("fcntl(fd, F_SETFL, nonb) failed"); } }
int http_find_and_replace(int accept_socket, HTTP_RECV_INFO *http_recv_info) { char *index_array[] = {"wizd.htm", "wizd.html", "index.htm", "index.html"}; char wizd_buf[256]; int i; SKIN_T *index_skin; char *p; SKIN_REPLASE_GLOBAL_DATA_T *skin_rep_data_global_p; extern void send_menu(int accept_socket, SKIN_REPLASE_GLOBAL_DATA_T *skin_rep_data_global_p, HTTP_RECV_INFO *http_recv_info_p, int skip); for (i = 0; i < 4; i++) { if (find_real_path(index_array[i], http_recv_info, wizd_buf)) { if (strncasecmp(index_array[i], "wizd", 4) == 0) { debug_log_output("HTTP: got wizd.htm\n"); http_send_ok_header(accept_socket, 0, NULL); index_skin = malloc(sizeof(SKIN_T)); index_skin->buffer = skin_file_read(wizd_buf, &index_skin->buffer_size); skin_rep_data_global_p = skin_create_global_data(http_recv_info, 0); skin_direct_replace_global(index_skin, skin_rep_data_global_p); if ((p = strstr(index_skin->buffer, SKIN_KEYWORD_MENU)) != 0) { // add in the menu (mid_*.html) info send(accept_socket, index_skin->buffer, p - index_skin->buffer, 0); send_menu(accept_socket, skin_rep_data_global_p, http_recv_info, 0); p += strlen(SKIN_KEYWORD_MENU); send(accept_socket, p, index_skin->buffer + strlen(index_skin->buffer) - p, 0); } else skin_direct_send(accept_socket, index_skin); skin_close(index_skin); free( skin_rep_data_global_p ); } else { debug_log_output("HTTP: got index.htm\n"); strcpy(http_recv_info->send_filename, wizd_buf); http_file_response(accept_socket, http_recv_info); } return(1); } } return(0); }
static int http_not_found_response(int accept_socket, HTTP_RECV_INFO *http_recv_info) { char buffer[1024]; sprintf(buffer, HTTP_NOT_FOUND1 HTTP_SERVER_NAME HTTP_CONTENT_TYPE HTTP_CONTENT_LENGTH HTTP_END "%s" ,SERVER_NAME , "text/html" , (size_t)9 ,"Not Found" ); send(accept_socket, buffer, strlen(buffer),0); debug_log_output("Not Found %s", http_recv_info->request_uri); return 0; }
// ************************************************************************** // 拡張子変更処理。削除用。 // extension_convert_listに従い、rename → org への変換を行う。 // // 例) "hogehoge.m2p.mpg" → "hogehoge.m2p" // ************************************************************************** void extension_del_rename(unsigned char *rename_filename_p, size_t rename_filename_size) { int i; unsigned char renamed_ext[WIZD_FILENAME_MAX]; unsigned char ext[WIZD_FILENAME_MAX]; if ( rename_filename_p == NULL ) return; for ( i=0; extension_convert_list[i].org_extension != NULL; i++ ) { debug_log_output("org='%s', rename='%s'" , extension_convert_list[i].org_extension , extension_convert_list[i].rename_extension); snprintf(renamed_ext, sizeof(renamed_ext), ".%s.%s" , extension_convert_list[i].org_extension , extension_convert_list[i].rename_extension); // 比較する拡張子と同じ長さにそろえる。 strncpy(ext, rename_filename_p, sizeof(ext)); cat_before_n_length(ext, strlen(renamed_ext)); // 拡張子一致? if ( strcasecmp(ext, renamed_ext) == 0 ) { debug_log_output(" HIT!!!" ); // 拡張子を「削除」 cat_after_n_length(rename_filename_p , strlen(extension_convert_list[i].rename_extension) + 1); debug_log_output("rename_filename_p='%s'", rename_filename_p); break; } } return; }
//****************************************************************** // EUC文字列を、n byteに削除。文字列境界を見て良きに計らう。 // もし menu_font_metric が指定されており menu_font_metric_string が // 指定されていれば、その値に従ってちょんぎる。 //****************************************************************** void euc_string_cut_n_length(unsigned char *euc_sentence, unsigned int n) { int euc_flag = 0; unsigned char *p, *prev_p; int total_len = 0; int size = 0; total_len = global_param.menu_font_metric * n; prev_p = p = euc_sentence; while (*p && total_len > 0) { if (*p >= sizeof(global_param.menu_font_metric_string)) { size = global_param.menu_font_metric; } else { char ch; ch = global_param.menu_font_metric_string[*p]; if (ch < 'a' || ch > 'z') { size = global_param.menu_font_metric; } else { size = ch - 'a'; if (size <= 0) { //debug_log_output("damn. old size: %d", size); size = global_param.menu_font_metric; } } } if (total_len < size) { if (euc_flag == 1) { *prev_p = '\0'; } else { *p = '\0'; } return ; } if (euc_flag == 0 && (( *p >= 0xA1 ) || ( *p == 0x8E ))) { // EUC-Code or 半角カタカナの上位 euc_flag = 1; } else { euc_flag = 0; } total_len -= size; prev_p = p++; } debug_log_output("debug: rest %d", total_len); *p = '\0'; return; }
// ************************************************************************** // 拡張子変更処理。追加用。 // extension_convert_listに従い、org → rename への変換を行う。 // // 例) "hogehoge.m2p" → "hogehoge.m2p.mpg" // ************************************************************************** void extension_add_rename(unsigned char *rename_filename_p, size_t rename_filename_size) { int i; unsigned char ext[WIZD_FILENAME_MAX]; if ( rename_filename_p == NULL ) return; filename_to_extension(rename_filename_p, ext, sizeof(ext)); if (strlen(ext) <= 0) return; for ( i=0; extension_convert_list[i].org_extension != NULL; i++ ) { debug_log_output("org='%s', rename='%s'" , extension_convert_list[i].org_extension , extension_convert_list[i].rename_extension); // 拡張子一致? if ( strcasecmp(ext, extension_convert_list[i].org_extension) == 0 ) { debug_log_output(" HIT!!!" ); // 拡張子を「追加」 strncat(rename_filename_p, "." , rename_filename_size - strlen(rename_filename_p)); strncat(rename_filename_p , extension_convert_list[i].rename_extension , rename_filename_size - strlen(rename_filename_p)); debug_log_output("rename_filename_p='%s'", rename_filename_p); break; } } return; }
// ************************************************************************** // accept_socketから、1行(CRLFか、LF単独が現れるまで)受信 // CRLFは削除する。 // 受信したサイズをreturnする。 // ************************************************************************** int line_receive(int accept_socket, unsigned char *line_buf_p, int line_max) { unsigned char byte_buf; int line_len; int recv_len; unsigned char *line_buf_work_p; line_buf_work_p = line_buf_p; line_len = 0; // 1行受信実行 while ( 1 ) { recv_len = recv(accept_socket, &byte_buf, 1, 0); if ( recv_len != 1 ) // 受信失敗チェック { //debug_log_output("line_receive: read_len == -1"); return ( -1 ); } // CR/LFチェック if ( byte_buf == '\r' ) { continue; } else if ( byte_buf == '\n' ) { break; } // バッファにセット *line_buf_work_p = byte_buf; line_buf_work_p++; line_len++; // printf("line_len=%d, buf='%s'\n", line_len, line_buf_p); // 受信バッファサイズチェック if ( line_len >= line_max) { // バッファオーバーフロー検知 debug_log_output("line_buf over flow."); return line_len; } } return line_len; }
int http_find_and_replace_html(int accept_socket, HTTP_RECV_INFO *http_recv_info) { char wizd_buf[256]; int i; int len; SKIN_T *index_skin; char *p; SKIN_REPLASE_GLOBAL_DATA_T *skin_rep_data_global_p; struct stat dir_stat; int found = 0; extern void send_menu(int accept_socket, SKIN_REPLASE_GLOBAL_DATA_T *skin_rep_data_global_p, HTTP_RECV_INFO *http_recv_info_p, int skip); for (i = 0; i < global_param.num_aliases; i++) { len = strlen(global_param.alias_name[i]); if (strncmp(&(http_recv_info->recv_uri[1]), global_param.alias_name[i], len) == 0) { strcpy(wizd_buf, global_param.alias_path[i]); strcat(wizd_buf, http_recv_info->recv_uri + len + 1); if (stat(wizd_buf, &dir_stat) == 0) { found = 1; break; } } } if (found == 0) return(0); debug_log_output("HTTP: got wizd.htm\n"); http_send_ok_header(accept_socket, 0, NULL); index_skin = malloc(sizeof(SKIN_T)); index_skin->buffer = skin_file_read(wizd_buf, &index_skin->buffer_size); skin_rep_data_global_p = skin_create_global_data(http_recv_info, 0); skin_direct_replace_global(index_skin, skin_rep_data_global_p); if ((p = strstr(index_skin->buffer, SKIN_KEYWORD_MENU)) != 0) { // add in the menu (mid_*.html) info send(accept_socket, index_skin->buffer, p - index_skin->buffer, 0); send_menu(accept_socket, skin_rep_data_global_p, http_recv_info, 0); p += strlen(SKIN_KEYWORD_MENU); send(accept_socket, p, index_skin->buffer + strlen(index_skin->buffer) - p, 0); } else skin_direct_send(accept_socket, index_skin); skin_close(index_skin); free( skin_rep_data_global_p ); return(1); }
// ************************************************************************** // SVI m2pファイルtotalサイズ読み込み // ************************************************************************** u_int64_t svi_file_total_size(unsigned char *svi_filename) { int fd; int i; off_t lseek_ret; ssize_t read_length; unsigned char total_size_work[SVI_TOTAL_SIZE_LENGTH]; u_int64_t total_size; // -------------------------------- // SVIファイルから情報取り出す // -------------------------------- // SVIファイル開く fd = open(svi_filename, O_RDONLY); if ( fd < 0 ) { debug_log_output("oepn() error."); return 0; } // SVIファイルのファイル位置をGET lseek_ret = lseek(fd, SVI_TOTAL_SIZE_OFFSET, SEEK_SET); if ( lseek_ret < 0 ) { debug_log_output("lseek() error."); return 0; } // SVIファイルから、m2pファイル 合計サイズをget read_length = read(fd, total_size_work, SVI_TOTAL_SIZE_LENGTH); debug_log_output("read_length=%d", read_length); debug_log_output("total_size=%02X,%02X,%02X,%02X,%02X" , total_size_work[0], total_size_work[1],total_size_work[2],total_size_work[3], total_size_work[4]); if (read_length != SVI_TOTAL_SIZE_LENGTH) { debug_log_output("read() error."); return 0; } close( fd ); total_size = 0; for ( i=0; i<SVI_TOTAL_SIZE_LENGTH; i++ ) { total_size += (u_int64_t)total_size_work[i] << (8 * i); } debug_log_output("total_size=%lld", total_size); return total_size; }
static int http_unauthorized_response(int accept_socket, HTTP_RECV_INFO *http_recv_info) { char buffer[WIZD_FILENAME_MAX]; snprintf(buffer, sizeof(buffer), "HTTP/1.1 401 Authorization Required\r\n" "WWW-Authenticate: Basic realm=NEED AUTHENTICATE!\r\n" "\r\n" ); debug_log_output("unauthorized response."); write(accept_socket, buffer, strlen(buffer)+1); return 0; /* return http_redirect_response(accept_socket, http_recv_info, "/"); */ }
// ************************************************************************** // ファイルの実体の送信実行部 // ************************************************************************** static off_t http_file_send(int accept_socket, unsigned char *filename, off_t content_length, off_t range_start_pos ) { off_t seek_ret; off_t written=0; // --------------------- // ファイルオープン // --------------------- if(global_param.buffer_size < 2) { FILE *fp = fopen(filename, "rb"); if ( fp == NULL ) { debug_log_output("errno = %s\n", strerror(errno)); debug_log_output("fopen() error. file = '%s'", filename); return ( 0 ); } seek_ret = fseek(fp, range_start_pos, SEEK_SET); if ( seek_ret < 0 ) // lseek エラーチェック { debug_log_output("fseek() error."); fclose(fp); return ( 0 ); } written = copy_FILE_to_descriptor(fp, accept_socket, content_length); fclose(fp); // File Close. } else { int fd = open(filename, O_RDONLY); if ( fd < 0 ) { debug_log_output("errno = %s\n", strerror(errno)); debug_log_output("open() error. file = '%s'", filename); return ( 0 ); } // ------------------------------------------ // range_start_posへファイルシーク // ------------------------------------------ seek_ret = lseek(fd, range_start_pos, SEEK_SET); if ( seek_ret < 0 ) // lseek エラーチェック { debug_log_output("lseek() error."); close(fd); return ( 0 ); } written = copy_descriptors(fd, accept_socket, content_length, NULL); close(fd); } // 正常終了 return written; }
static int next_file(int *in_fd_p, JOINT_FILE_INFO_T *joint_file_info_p) { off_t ret; if (in_fd_p && joint_file_info_p) { // 読み終わったファイルをCLOSE() debug_log_output("[%02d] '%s' close()", joint_file_info_p->current_file_num, joint_file_info_p->file[joint_file_info_p->current_file_num].name); close(*in_fd_p); // 次のファイルがあるか? joint_file_info_p->current_file_num++; if ( joint_file_info_p->current_file_num >= joint_file_info_p->file_num ) { debug_log_output("EOF Detect."); //printf("EOF Detect."); return 1; // これで終了 } // 次のファイルをOPEN() debug_log_output("[%02d] '%s' open(), start_pos %lld\n", joint_file_info_p->current_file_num, joint_file_info_p->file[joint_file_info_p->current_file_num].name, joint_file_info_p->file[joint_file_info_p->current_file_num].start_pos); //printf("[%02d] '%s' open(), start_pos %lld, size %lld\n", // joint_file_info_p->current_file_num, // joint_file_info_p->file[joint_file_info_p->current_file_num].name, // joint_file_info_p->file[joint_file_info_p->current_file_num].start_pos, // joint_file_info_p->file[joint_file_info_p->current_file_num].size // ); *in_fd_p = open(joint_file_info_p->file[joint_file_info_p->current_file_num].name, O_RDONLY); if ( *in_fd_p < 0 ) { debug_log_output("errno = %s\n", strerror(errno)); debug_log_output("open() error. '%s'", joint_file_info_p->file[joint_file_info_p->current_file_num].name); return ( -1 ); } ret = lseek(*in_fd_p, joint_file_info_p->file[joint_file_info_p->current_file_num].start_pos, SEEK_SET); debug_log_output("seek to %lld returned %lld\n", joint_file_info_p->file[joint_file_info_p->current_file_num].start_pos, ret); //printf("seek to %lld returned %lld\n", joint_file_info_p->file[joint_file_info_p->current_file_num].start_pos, ret); // ブロックモードの設定 set_blocking_mode(*in_fd_p, 0); /* blocking */ return 0; // 次のファイルの準備完了 } else { // パラメータがNULLの場合には1ファイルのみの処理とする return 1; // これで終了 } }
// ************************************************************************** // * SJIS文字列の中から、codeをを含む文字(2byte文字)を、固定文字列で置換する。 // ************************************************************************** void sjis_code_thrust_replase(unsigned char *sentence, const unsigned char code) { int i; unsigned char rep_code[2]; // 置換する文字列。(SJISで'*') rep_code[0] = 0x81; rep_code[1] = 0x96; // 文字列長が、1byte以下なら、処理必要なし。 if ( strlen(sentence) <= 1 ) { return; } // 置換対象文字 捜索 for ( i=1; sentence[i] != '\0' ;i++ ) { // code にヒット? if ( sentence[i] == code ) { // 1byte前が、SJIS 1byte目の範囲?(0x81〜0x9F、0xE0〜〜0xFC) if ((( sentence[i-1] >= 0x81 ) && (sentence[i-1] <= 0x9F)) || (( sentence[i-1] >= 0xE0 ) && (sentence[i-1] <= 0xFC)) ) { debug_log_output("SJIS Replase HIT!!!!"); // 置換実行 sentence[i-1] = rep_code[0]; sentence[i ] = rep_code[1]; } } } return; }
off_t copy_descriptors(int in_fd, int out_fd, off_t content_length, JOINT_FILE_INFO_T *joint_file_info_p) { int i; struct _buff { int inuse; int pos; int len; unsigned char *p; } *buff; unsigned char *p; int read_idx = 0; int write_idx = 0; int idx_count = 0; off_t read_cnt = 0; off_t total_read_size = 0; size_t target_read_size; int len; int flag_finish = 0; int flag_first_time = 0; int flag_verbose = 0; time_t first_timeout = 0; off_t written = 0; struct timeb marker1,marker2; //,marker3; int marker_count; if(global_param.buffer_size < 1) global_param.buffer_size = 1; if(global_param.stream_chunk_size < 512) global_param.stream_chunk_size = 512; ssize_t blocks_read = 0; int offset = 0; if (joint_file_info_p) offset = joint_file_info_p->iso_seek; // ====================== // 送信バッファを確保 // ====================== debug_log_output("Allocating %d buffers of %d bytes each\n", global_param.buffer_size, global_param.stream_chunk_size); buff = malloc(global_param.buffer_size * sizeof(struct _buff)); if ( buff == NULL ) { debug_log_output("malloc() error.\n"); return (-1 ); } p = malloc(global_param.buffer_size * global_param.stream_chunk_size); if ( p == NULL ) { debug_log_output("malloc() error.\n"); return ( 0 ); } for (i=0; i<global_param.buffer_size; i++) { buff[i].pos = 0; buff[i].inuse = 0; buff[i].len = 0; buff[i].p = p + i*global_param.stream_chunk_size; } // ブロックモードの設定 //set_blocking_mode(in_fd, 0); /* blocking */ set_blocking_mode(out_fd, (global_param.buffer_size>1)); /* non-blocking if multiple buffers */ //debug_log_output("set non-blocking mode"); // Do the calculation this way so global_param.buffer_size<4 always gets flag_first_time //No, don't force this!!! : if (content_length < global_param.stream_chunk_size * (global_param.buffer_size>>2)) flag_first_time = 1; if (global_param.buffer_size < 4) flag_first_time = 1; // If only one buffer, then we work the same as the http_simple_file_send() if (global_param.buffer_size == 1) global_param.flag_buffer_send_asap = 1; ftime(&marker1); marker_count = 0; first_timeout = time(NULL); // ================ // 実体転送開始 // ================ while ( flag_finish < 2 ) { struct timeval tv; fd_set writefds; if (flag_first_time == 0 && first_timeout + 13 <= time(NULL)) { debug_log_output( "****************************************************\n" "** Low bandwidth? send it anyway... **\n" "****************************************************"); flag_first_time = 1; } if (flag_finish || flag_first_time || (idx_count >= (global_param.buffer_size-2))) { if (flag_first_time == 0) { debug_log_output( "*********************************************************\n" "** CACHE FILLED! **\n" "*********************************************************"); flag_first_time = 1; } while(buff[write_idx].inuse) { // If there is nothing more to read, concentrate on writing FD_ZERO(&writefds); FD_SET(out_fd, &writefds); tv.tv_sec = 0; if (flag_finish == 0 && !buff[read_idx].inuse) { // Don't wait for the select because we have room to read tv.tv_usec = 0; } else { tv.tv_usec = 100000; // 100msec maximum wait = 10Hz polling rate } //ftime(&marker2); i = select(FD_SETSIZE, NULL, &writefds, NULL, &tv); //ftime(&marker3); //len = (marker3.time-marker2.time)*1000 + (marker3.millitm-marker2.millitm); //if(len>9) fputc('0'+len/10, stderr); if (i < 0) { // Select returned an error - socket must be closed debug_log_output("select failed. err = %s", strerror(errno)); flag_finish = 2; break; } else if(i > 0) { //ftime(&marker2); len = write(out_fd, buff[write_idx].p + buff[write_idx].pos, (buff[write_idx].len < global_param.socket_chunk_size) ? buff[write_idx].len : global_param.socket_chunk_size ); //ftime(&marker3); //i = (marker3.time-marker2.time)*1000 + (marker3.millitm-marker2.millitm); //if(i>9) fputc('a'+i/10, stderr); if(len > 0) { //fputc('.', stderr); written += len; buff[write_idx].len -= len; marker_count += len; if(!global_param.flag_daemon && (marker_count > 2000000)) { // Display the network transfer rate double rate; ftime(&marker2); rate = (marker2.time-marker1.time) + 0.001*(marker2.millitm-marker1.millitm); if(rate > 0) { rate = 8e-6 * marker_count / rate; /* fprintf(stderr, "%g Mbps\n", rate); */ } marker1 = marker2; marker_count = 0; } if (flag_verbose) { debug_log_output("sent: len =%6d, idx = %4d, idxcount = %4d", len, write_idx, idx_count); } else if(0 && !global_param.flag_daemon) { show_progress(idx_count * 100 / global_param.buffer_size, idx_count); } if (buff[write_idx].len <= 0) { buff[write_idx].inuse = 0; buff[write_idx].len = 0; buff[write_idx].pos = 0; write_idx = (write_idx + 1) % global_param.buffer_size; idx_count --; } else { buff[write_idx].pos += len; } } else { //fputc('-', stderr); // Failed to write - end the stream if (len < 0) { if(errno == EAGAIN) { debug_log_output("write would block"); break; } debug_log_output("write failed after %d bytes. err = %s (%d)", written, strerror(errno), (int)errno); } else { debug_log_output("socket closed by player"); } flag_finish = 2; break; } } else { // Not ready to write, exit from the loop to do a read //if (flag_finish == 0 && !buff[read_idx].inuse) { // fputc(',', stderr); //} else { // fputc('o', stderr); //} break; } } if((flag_finish==1) && !buff[write_idx].inuse) { flag_finish = 2; debug_log_output( "*********************************************************\n" "** SEND FINISHED! **\n" "*********************************************************"); } } // Always attempt a read if we have a buffer available //if (FD_ISSET(in_fd, &readfds)) { if(flag_finish == 0 && !buff[read_idx].inuse) { // target_read_size = (content_length - total_read_size) > 1024 ? 1024 : (content_length - total_read_size); target_read_size = global_param.stream_chunk_size - buff[read_idx].len; /* if (buff[read_idx].p == NULL) { debug_log_output("error! idx: %d", read_idx); } */ ftime(&marker2); if ((joint_file_info_p!=NULL) && (joint_file_info_p->dvd_file != NULL)) { blocks_read = DVDReadBlocks(joint_file_info_p->dvd_file, offset, target_read_size/2048, buff[read_idx].p + buff[read_idx].len); len = (off_t)blocks_read*2048; offset += blocks_read; } else if (joint_file_info_p && read_cnt >= joint_file_info_p->file[joint_file_info_p->current_file_num].size) { len = 0; read_cnt = 0; debug_log_output("finished file chunk %d\n", joint_file_info_p->current_file_num); //printf("finished file chunk %d\n", joint_file_info_p->current_file_num); } else { if (joint_file_info_p && target_read_size + read_cnt > joint_file_info_p->file[joint_file_info_p->current_file_num].size) { target_read_size = joint_file_info_p->file[joint_file_info_p->current_file_num].size - read_cnt; debug_log_output("finishing last block of %d\n", joint_file_info_p->current_file_num); //printf("finishing last block of %d\n", joint_file_info_p->current_file_num); } len = read(in_fd, buff[read_idx].p + buff[read_idx].len, target_read_size); read_cnt += len; } //ftime(&marker3); //i = (marker3.time-marker2.time)*1000 + (marker3.millitm-marker2.millitm); //if(i>9) fputc('A'+i/10, stderr); if(len == 0) { if( (joint_file_info_p==NULL) || (joint_file_info_p->dvd_file != NULL) || next_file(&in_fd, joint_file_info_p)) { // 読み込み終わり flag_finish = 1; if (flag_verbose) { debug_log_output("recv: len = %d, idx = %d finish!", len, read_idx); } else { debug_log_output( "*********************************************************\n" "** RECV FINISHED! **\n" "*********************************************************"); } if (buff[read_idx].len > 0) { buff[read_idx].inuse = 1; buff[read_idx].pos = 0; } } else { // 次のファイルに続く(ここでは何もしない) } } else if (len > 0) { //fputc(':', stderr); if (flag_verbose) { debug_log_output("recv: len =%6d, idx = %4d, idxcount = %4d", len, read_idx, idx_count); } else if(0 && !global_param.flag_daemon) { show_progress(idx_count * 100 / global_param.buffer_size, idx_count); } buff[read_idx].len += len; total_read_size += len; if (global_param.flag_buffer_send_asap == TRUE || buff[read_idx].len >= global_param.stream_chunk_size) { buff[read_idx].inuse = 1; buff[read_idx].pos = 0; idx_count ++; read_idx = (read_idx + 1) % global_param.buffer_size; } /* if (content_length - total_read_size <= 0) { flag_finish = 1; } */ } else { flag_finish = 1; if (flag_verbose) { debug_log_output("read err?: len = %d, idx = %d, err: %s", len, read_idx, strerror(errno)); } else { debug_log_output( "*********************************************************\n" "** RECV FINISHED!(ret = %d) **\n" "*********************************************************", len); } } } } free(p); free(buff); // Memory Free. debug_log_output("copy descriptors end."); // 正常終了 return written; }
// ************************************************************************** // HTTPヘッダを受信して解析する。 // // 処理するのはGETのみ。GET以外のメソッドが来たらエラー // 今のところ、URIとuser_agent、Range、Hostを解析。 // URIは、URIデコードもやる。 // // return: 0 正常終了 // return: 0以外 エラー // ************************************************************************** static int http_header_receive(int accept_socket, HTTP_RECV_INFO *http_recv_info_p) { int result = 0; int recv_len; unsigned char line_buf[1024]; // 大きめに。 unsigned char work_buf[1024]; unsigned char work_buf2[1024]; unsigned char split1[1024]; unsigned char split2[1024]; int ret; int i; int j; // ================================ // 1行づつ HTTPヘッダを受信 // ================================ for (i=0;;i++) { // 1行受信 実行。 memset(line_buf, '\0', sizeof(line_buf)); recv_len = line_receive(accept_socket, line_buf, sizeof(line_buf)); // debug. 受信したヘッダ表示 debug_log_output("'%s'(%d byte)\n", line_buf, recv_len ); // 受信した内容をチェック。 if ( i != 0 && recv_len == 0 ) // 空行検知。ヘッダ受信終了。 { break; } else if ( recv_len < 0 ) // 受信失敗 { return ( -1 ); } // -------------------------- // GETメッセージチェック // -------------------------- if ( i == 0 ) // 1行目のみチェック { debug_log_output("URI Check start.'%s'\n", line_buf); // GET/POSTある? if (strncmp(line_buf, "GET ", 4) && strncmp(line_buf, "POST ", 5)) { debug_log_output("'GET' or 'POST' not found. error."); return ( -1 ); } strncpy(http_recv_info_p->request_method, line_buf, sizeof(http_recv_info_p->request_method)); cut_after_character(http_recv_info_p->request_method, ' '); // 最初のスペースまでを削除。 cut_before_character(line_buf, ' '); // 次にスペースが出てきたところの後ろまでを削除。 cut_after_character(line_buf, ' '); // =========================== // GETオプション部解析 // =========================== // REQUEST_URI用・Proxy用に値を保存 strncpy(http_recv_info_p->request_uri, line_buf, sizeof(http_recv_info_p->request_uri)); // '?'が存在するかチェック。 if ( strchr(line_buf, '?') != NULL ) { strncpy(work_buf, line_buf, sizeof(work_buf)); // '?'より前をカット cut_before_character(work_buf, '?' ); debug_log_output("work_buf = '%s'", work_buf ); while ( 1 ) { memset(split1, '\0', sizeof(split1)); memset(split2, '\0', sizeof(split2)); uri_decode(split1, sizeof(split1), work_buf, sizeof(work_buf) ); strcpy(work_buf, split1); // 最初に登場する'&'で分割 ret = sentence_split(work_buf, '&', split1, split2 ); if ( ret == 0 ) // 分割成功 { strncpy(work_buf, split2, sizeof(work_buf)); } else if (strlen(work_buf) > 0) // まだwork_bufに中身ある? { strncpy( split1, work_buf, sizeof(split1)); strncpy( work_buf, "", sizeof(work_buf)); } else // 処理終了 { break; } // ------------------------------------- // GETした内容 解析開始 // 超安直。いいのかこんな比較で。 // ------------------------------------- // URIデコード // uri_decode(work_buf2, sizeof(work_buf2), split1, sizeof(split1) ); strcpy(work_buf2, split1); // "page="あるか調査。 if (strncasecmp( work_buf2, "page=", strlen("page=") ) == 0 ) { // = より前を削除 cut_before_character(work_buf2, '='); if ( strlen(work_buf2) > 0 ) { // 構造体に値を保存。 http_recv_info_p->page = atoi(work_buf2); } continue; } // "menupage="あるか調査。 if (strncasecmp( work_buf2, "menupage=", strlen("menupage=") ) == 0 ) { // = より前を削除 cut_before_character(work_buf2, '='); if ( strlen(work_buf2) > 0 ) { // 構造体に値を保存。 http_recv_info_p->menupage = atoi(work_buf2); } continue; } // "title=" DVD title selection if (strncasecmp( work_buf2, "title=", strlen("title=") ) == 0 ) { // = より前を削除 cut_before_character(work_buf2, '='); if ( strlen(work_buf2) > 0 ) { // 構造体に値を保存。 http_recv_info_p->title = atoi(work_buf2); } continue; } if (strncasecmp( work_buf2, "width=", strlen("width=") ) == 0 ) { // = より前を削除 cut_before_character(work_buf2, '='); if ( strlen(work_buf2) > 0 ) { // 構造体に値を保存。 global_param.target_jpeg_width = atoi(work_buf2); } continue; } if (strncasecmp( work_buf2, "height=", strlen("height=") ) == 0 ) { // = より前を削除 cut_before_character(work_buf2, '='); if ( strlen(work_buf2) > 0 ) { // 構造体に値を保存。 global_param.target_jpeg_height = atoi(work_buf2); } continue; } // "action="あるか調査。 if (strncasecmp( work_buf2, "action=", strlen("action=") ) == 0 ) { // = より前を削除 cut_before_character(work_buf2, '='); // 構造体に値を保存。 strncpy(http_recv_info_p->action, work_buf2, sizeof(http_recv_info_p->action)); continue; } // "type=" allplay list type if (strncasecmp( work_buf2, "type=movie", strlen("type=movie") ) == 0 ) { http_recv_info_p->default_file_type = TYPE_MOVIE; continue; } if (strncasecmp( work_buf2, "type=music", strlen("type=music") ) == 0 ) { http_recv_info_p->default_file_type = TYPE_MUSIC; continue; } if (strncasecmp( work_buf2, "type=photo", strlen("type=photo") ) == 0 ) { http_recv_info_p->default_file_type = TYPE_JPEG; continue; } if (strncasecmp( work_buf2, "type=soundtrack", strlen("type=soundtrack") ) == 0 ) { http_recv_info_p->default_file_type = TYPE_MUSICLIST; continue; } if (strncasecmp( work_buf2, "type=slideshow", strlen("type=slideshow") ) == 0 ) { http_recv_info_p->default_file_type = TYPE_PLAYLIST; continue; } // "option="あるか調査 if (strncasecmp( work_buf2, "option=", strlen("option=") ) == 0 ) { // = より前を削除 cut_before_character(work_buf2, '='); // 構造体に値を保存。 strncpy(http_recv_info_p->option, work_buf2, sizeof(http_recv_info_p->option)); continue; } // "alias="あるか調査 if (strncasecmp( work_buf2, "alias=", strlen("alias=") ) == 0 ) { // = より前を削除 cut_before_character(work_buf2, '='); // 構造体に値を保存。 strncpy(http_recv_info_p->alias, work_buf2, sizeof(http_recv_info_p->alias)); if (strncasecmp(http_recv_info_p->alias, "movie", strlen("movie")) == 0) http_recv_info_p->default_file_type = TYPE_MOVIE; else if (strncasecmp(http_recv_info_p->alias, "music", strlen("music")) == 0) http_recv_info_p->default_file_type = TYPE_MUSIC; else if (strncasecmp(http_recv_info_p->alias, "photo", strlen("photo")) == 0) http_recv_info_p->default_file_type = TYPE_JPEG; else http_recv_info_p->default_file_type = TYPE_UNKNOWN; continue; } if (strncasecmp( work_buf2, "lsearch=", strlen("lsearch=") ) == 0 ) { cut_before_character(work_buf2, '='); strcpy(http_recv_info_p->lsearch, work_buf2); strcpy(http_recv_info_p->recv_uri, work_buf2); continue; } // "search= if (strncasecmp( work_buf2, "search", strlen("search") ) == 0 ) { if (strncasecmp(work_buf2, "search_movie", strlen("search_movie")) == 0) { http_recv_info_p->search_type = TYPE_MOVIE; strcpy(http_recv_info_p->search_str, "_movie"); http_recv_info_p->default_file_type = TYPE_MOVIE; // printf("search movie for "); } else if (strncasecmp(work_buf2, "search_music", strlen("search_music")) == 0) { http_recv_info_p->search_type = TYPE_MUSIC; strcpy(http_recv_info_p->search_str, "_music"); http_recv_info_p->default_file_type = TYPE_MUSIC; // printf("search music for "); } else if (strncasecmp(work_buf2, "search_photo", strlen("search_photo")) == 0) { http_recv_info_p->search_type = TYPE_JPEG; strcpy(http_recv_info_p->search_str, "_photo"); http_recv_info_p->default_file_type = TYPE_JPEG; // printf("search photo for "); } else if (strncasecmp(work_buf2, "search_all", strlen("search_all")) == 0) { http_recv_info_p->search_type = TYPE_UNKNOWN; strcpy(http_recv_info_p->search_str, "_all"); http_recv_info_p->default_file_type = TYPE_UNKNOWN; // printf("search all for "); } else continue; cut_before_character(work_buf2, '='); strncpy(http_recv_info_p->search, work_buf2, sizeof(http_recv_info_p->search)); if (http_recv_info_p->search[0] == '\0') // everything qualifies strcpy(http_recv_info_p->search, ".*"); // printf("%s\n", http_recv_info_p->search); continue; } // "sort="あるか調査 if (strncasecmp( work_buf2, "sort=", strlen("sort=") ) == 0 ) { // = より前を削除 cut_before_character(work_buf2, '='); // 構造体に値を保存。 strncpy(http_recv_info_p->sort, work_buf2, sizeof(http_recv_info_p->sort)); continue; } // "dvdopt="あるか調査 if (strncasecmp( work_buf2, "dvdopt=", strlen("dvdopt=") ) == 0 ) { // = より前を削除 cut_before_character(work_buf2, '='); // 構造体に値を保存。 strncpy(http_recv_info_p->dvdopt, work_buf2, sizeof(http_recv_info_p->dvdopt)); continue; } // "focus="あるか調査 if (strncasecmp( work_buf2, "focus=", strlen("focus=") ) == 0 ) { // = より前を削除 cut_before_character(work_buf2, '='); // 構造体に値を保存。 strncpy(http_recv_info_p->focus, work_buf2, sizeof(http_recv_info_p->focus)); continue; } } } debug_log_output("http_recv_info_p->page = '%d'", http_recv_info_p->page); debug_log_output("http_recv_info_p->title = '%d'", http_recv_info_p->title); debug_log_output("http_recv_info_p->action = '%s'", http_recv_info_p->action); debug_log_output("http_recv_info_p->option = '%s'", http_recv_info_p->option); debug_log_output("http_recv_info_p->dvdopt = '%s'", http_recv_info_p->dvdopt); // URIデコード cut_after_character(line_buf, '?'); uri_decode(work_buf, sizeof(work_buf), line_buf, sizeof(line_buf) ); strncpy(line_buf, work_buf, sizeof(line_buf)); debug_log_output("URI(decoded):'%s'\n", line_buf); convert_language_code(line_buf, work_buf, sizeof(work_buf), CODE_AUTO, CODE_EUC); debug_log_output("URI(decoded,euc,FYI):'%s'\n", work_buf); // 構造体に保存 if (http_recv_info_p->lsearch[0] == '\0') strncpy(http_recv_info_p->recv_uri, line_buf, sizeof(http_recv_info_p->recv_uri)); continue; } // User-agent切り出し if ( strncasecmp(line_buf, HTTP_USER_AGENT, strlen(HTTP_USER_AGENT) ) == 0 ) { // ':'より前を切る cut_before_character(line_buf, ':'); cut_first_character(line_buf, ' '); // 構造体に保存 strncpy( http_recv_info_p->user_agent, line_buf, sizeof(http_recv_info_p->user_agent)); // Set the skin name based on user agent, if desired for(j=0; j<global_param.alternate_skin_count; j++) { debug_log_output("Checking for '%s'", global_param.alternate_skin_match[j]); if(strstr(line_buf, global_param.alternate_skin_match[j]) != NULL) { strcpy(global_param.skin_name, global_param.alternate_skin_name[j]); debug_log_output("User agent matches alternate skin '%s'", global_param.skin_name); break; } } continue; } // Rangeあるかチェック if ( strncasecmp(line_buf, HTTP_RANGE, strlen(HTTP_RANGE) ) == 0 ) { debug_log_output("%s Detect.\n", HTTP_RANGE); // ':' より前を切る。 cut_before_character(line_buf, ':'); cut_first_character(line_buf, ' '); // recv_range にRangeの中身保存 strncpy(http_recv_info_p->recv_range, line_buf, sizeof(http_recv_info_p->recv_range)); // '=' より前を切る cut_before_character(line_buf, '='); // '-'で前後に分割。 sentence_split(line_buf, '-', work_buf, work_buf2); debug_log_output("work_buf='%s'\n", work_buf); debug_log_output("work_buf2='%s'\n", work_buf2); // 値を文字列→数値変換 http_recv_info_p->range_start_pos = strtoull(work_buf, NULL, 10); if ( strlen(work_buf2) > 0 ) { http_recv_info_p->range_end_pos = strtoull(work_buf2, NULL, 10); } debug_log_output("range_start_pos=%d\n", http_recv_info_p->range_start_pos); debug_log_output("range_end_pos=%d\n", http_recv_info_p->range_end_pos); continue; } // Hostあるかチェック if ( strncasecmp(line_buf, HTTP_HOST, strlen(HTTP_HOST) ) == 0 ) { debug_log_output("%s Detect.\n", HTTP_HOST); // ':' より前を切る。 cut_before_character(line_buf, ':'); cut_first_character(line_buf, ' '); strncpy(http_recv_info_p->recv_host, line_buf, sizeof(http_recv_info_p->recv_host)); if(NULL == strchr(http_recv_info_p->recv_host, ':')) { debug_log_output("CLIENT BUG: Host header field was missing port number - fixing"); snprintf(http_recv_info_p->recv_host + strlen(http_recv_info_p->recv_host), sizeof(http_recv_info_p->recv_host)-1, ":%d", global_param.server_port); debug_log_output("%s '%s'", HTTP_HOST, http_recv_info_p->recv_host); } continue; } if (strncasecmp(line_buf, HTTP_AUTHORIZATION, strlen(HTTP_AUTHORIZATION)) == 0) { debug_log_output("%s Detect.\n", HTTP_AUTHORIZATION); // ':' より前を切る。 cut_before_character(line_buf, ':'); cut_first_character(line_buf, ' '); if (strncmp(line_buf, "Basic ", 6)) { debug_log_output("received '%s', is not supported.", line_buf); continue; } strncpy(http_recv_info_p->passwd, line_buf + 6, sizeof(http_recv_info_p->passwd)); continue; } // Content-Lengthあるかチェック if ( strncasecmp(line_buf, HTTP_CONTENT_LENGTH_STR, strlen(HTTP_CONTENT_LENGTH_STR) ) == 0 ) { debug_log_output("%s Detect.\n", HTTP_CONTENT_LENGTH_STR); // ':' より前を切る。 cut_before_character(line_buf, ':'); cut_first_character(line_buf, ' '); /* recv_content_length は わざと long です. */ http_recv_info_p->recv_content_length = strtol(line_buf, NULL, 10); debug_log_output("Content-Length: %ld", http_recv_info_p->recv_content_length); continue; } } return result; }
// ************************************************************************** // * サーバ HTTP処理部 // ************************************************************************** void server_http_process(int accept_socket) { int result; HTTP_RECV_INFO http_recv_info; //HTTP受信情報保存構造体 int i; int flag_allow_user_agent_check; int found; memset(&http_recv_info, 0, sizeof(http_recv_info)); // ---------------------------------------- // HTTP リクエストヘッダ受信 // ---------------------------------------- debug_log_output("HTTP Header receive start!\n"); result = http_header_receive(accept_socket, &http_recv_info); if ( result != 0 ) // エラーチェック { // エラーメッセージ debug_log_output("http_header_receive() Error. result=%d\n", result); // ソケットクローズ close(accept_socket); return; } debug_log_output("HTTP Header receive end!\n"); debug_log_output("recv_uri:'%s'\n", http_recv_info.recv_uri); debug_log_output("user_agent:'%s'\n", http_recv_info.user_agent); debug_log_output("range_start_pos=%lld\n", http_recv_info.range_start_pos); debug_log_output("range_end_pos=%lld\n", http_recv_info.range_end_pos); if (http_recv_info.passwd[0]) { debug_log_output("http passwd:'%s'\n", http_recv_info.passwd); } // ========================== // = User-Agent チェック // ========================== flag_allow_user_agent_check = 0; if ( strlen(allow_user_agent[0].user_agent) == 0 ) // User-Agnet指示が無し。 { debug_log_output("user_agent: allow_user_agent No List. All Allow."); flag_allow_user_agent_check = 1; // OK } else { // User-Agent チェック実行 for ( i=0; i<ALLOW_USER_AGENT_LIST_MAX; i++) { if ( strlen(allow_user_agent[i].user_agent) == 0 ) { break; } // 一致チェック debug_log_output("user_agent: Check[%d] '%s' in '%s'",i, allow_user_agent[i].user_agent, http_recv_info.user_agent); if ( strstr( http_recv_info.user_agent, allow_user_agent[i].user_agent ) != NULL ) { debug_log_output("user_agent: '%s' OK.", allow_user_agent[i].user_agent ); flag_allow_user_agent_check = 1; // 一致。OK break; } } } if (global_param.http_passwd[0] && !flag_allow_user_agent_check) { // パスワードが設定されて、正しければ、User-AgentチェックNGでもOK. char *pass, *p; pass = base64(global_param.http_passwd); if ((p = strchr(pass, '=')) != NULL) { *p = '\0'; } if ((p = strchr(http_recv_info.passwd, '=')) != NULL) { *p = '\0'; } if (!strcmp(pass, http_recv_info.passwd)) { // 一致 flag_allow_user_agent_check = 1; } else { // 不一致 // flag_allow_user_agent_check = 0; http_unauthorized_response(accept_socket, &http_recv_info); close(accept_socket); return ; } } // User-Agentチェック NGならば、ソケットクローズ。終了。 if ( flag_allow_user_agent_check == 0 ) { debug_log_output("allow_user_agent check. Deny. Socket close."); // ソケットクローズ close(accept_socket); return; } // クライアントがPCかどうか判断 http_recv_info.flag_pc = (global_param.user_agent_pc[0] && !strncmp(http_recv_info.user_agent, global_param.user_agent_pc , strlen(global_param.user_agent_pc)) ) ? 1 : 0; if(global_param.user_agent_pc[0]) { debug_log_output("Checking for PC user agent '%s'", global_param.user_agent_pc); } debug_log_output("flag_pc: %d", http_recv_info.flag_pc); // User-Agent // HD: 'Syabas/13-14-060414-04-IOD-234-000/04-IOD (uCOS-II v2.05;NOS;KA9Q; Res1280x720-HiColor; TV Res1920x1080; Browser Res1104x656-8bits; www.syabas.com mac_addr=00.a0.b0.65.51.31)'(186 byte) // SD: 'Syabas/13-14-060414-04-IOD-234-000/04-IOD (uCOS-II v2.05;NOS;KA9Q; Res624x416-HiColor; TV Res1920x1080; Browser Res624x416-8bits; www.syabas.com mac_addr=00.a0.b0.65.51.31)' http_recv_info.flag_hd = (strstr(http_recv_info.user_agent, "Res624x416") == NULL); if (!strncmp(http_recv_info.recv_uri, "/-.-", 4)) { // proxy if (http_proxy_response(accept_socket, &http_recv_info) < 0) { http_not_found_response(accept_socket, &http_recv_info); } // ソケットクローズ close(accept_socket); return; } if (path_sanitize(http_recv_info.recv_uri, sizeof(http_recv_info.recv_uri)) == NULL) { // BAD REQUEST! debug_log_output("BAD REQUEST!"); http_not_found_response(accept_socket, &http_recv_info); close(accept_socket); return; } debug_log_output("sanitized recv_uri: %s", http_recv_info.recv_uri); // ---------------------------------------- // 受け取ったURIの拡張子がrename対象ならばrename // ---------------------------------------- extension_del_rename(http_recv_info.recv_uri, sizeof(http_recv_info.recv_uri)); // ============================ // ファイルチェック // 種類に応じて分岐 // ============================ result = http_file_check(&http_recv_info); debug_log_output("http_file_check returns %d\n", result); if (result != CGI_TYPE && !strcmp(http_recv_info.request_method, "POST")) { debug_log_output("BAD POST REQUEST."); http_not_found_response(accept_socket, &http_recv_info); } else if (result == -2) { // Directory without the trailing slash -> redirect char buffer[WIZD_FILENAME_MAX]; sprintf(buffer, "%s/", http_recv_info.recv_uri); http_redirect_response(accept_socket, &http_recv_info, buffer); } else if ( result < 0 ) // File not found { http_not_found_response(accept_socket, &http_recv_info); } else if ( result == GENERAL_FILE_TYPE ) // ファイル実体ならば、実体転送。 { if ( global_param.flag_allow_delete && (strcasecmp(http_recv_info.action, "delete" ) == 0) ) { // ---------------------------------------- // Delete file // ---------------------------------------- char path[WIZD_FILENAME_MAX]; debug_log_output("Delete file start!\n"); debug_log_output("unlink(%s)", http_recv_info.send_filename); unlink(http_recv_info.send_filename); // Remove any associated bookmarks if they exist snprintf(path, sizeof(path), "%s.wizd.bookmark", http_recv_info.send_filename); unlink(path); // Redirect to directory strncpy(path, http_recv_info.recv_uri, sizeof(path)); cut_after_last_character(path, '/'); strcat(path, "/"); http_redirect_response(accept_socket, &http_recv_info, path); debug_log_output("Delete file end!\n"); } else // actionに、ImageViewerが指示されている? if ( strcasecmp(http_recv_info.action, "ImageView" ) == 0) { // ---------------------------------------- // イメージファイルビューアー // ---------------------------------------- debug_log_output("Image Viewer start!\n"); http_image_viewer(accept_socket, &http_recv_info); debug_log_output("Image Viewer end!\n"); } // actionに、SinglePlayが指示されている? else if ( strcasecmp(http_recv_info.action, "SinglePlay" ) == 0) { // ---------------------------------------- // Musicファイル 単独プレイ // ---------------------------------------- debug_log_output("Single Play start!\n"); http_music_single_play(accept_socket, &http_recv_info); debug_log_output("Single Play end!\n"); } #ifdef RESIZE_JPEG // actionに、Resizeが指示されている? else if ( strcasecmp(http_recv_info.action, "Resize.jpg" ) == 0) { // ---------------------------------------- // JPEG Resize // ---------------------------------------- debug_log_output("JPEG Resize start!\n"); if(http_send_resized_jpeg(accept_socket, &http_recv_info) == 0) { // Failed to resize - send the original file as-is // (this can happen if we have the wrong file extension) http_file_response(accept_socket, &http_recv_info); } debug_log_output("JPEG Resize end!\n"); } #endif else if (strcmp(http_recv_info.option, "aviinfo") == 0) { debug_log_output("got aviinfo option\n"); http_menu(accept_socket, &http_recv_info); } else if (strcmp(http_recv_info.option, "mp3info") == 0) { http_menu(accept_socket, &http_recv_info); } else // アクションに指定無し。 { // ---------------------------------------- // ファイルの実体 // HTTPリクエストヘッダに従ってデータを返信。 // ---------------------------------------- found = 0; if (global_param.flag_use_index && strstr(http_recv_info.recv_uri, "wizd") != 0 && strstr(http_recv_info.recv_uri, ".htm") != 0) { if (http_find_and_replace_html(accept_socket, &http_recv_info)) { found = 1; } } if (!found) { debug_log_output("HTTP response start!\n"); http_file_response(accept_socket, &http_recv_info); debug_log_output("HTTP response end!\n"); } } } else if ( result == SVI_TYPE ) { // ---------------------------------------- // SVIファイル(;´Д`) // SVIファイル処理してデータを返信 // ---------------------------------------- // actionに、SinglePlayが指示されている? if ( strcasecmp(http_recv_info.action, "SinglePlay" ) == 0) { // ---------------------------------------- // Musicファイル 単独プレイ // ---------------------------------------- debug_log_output("Single Play start!(SVI)\n"); http_music_single_play(accept_socket, &http_recv_info); debug_log_output("Single Play end!(SVI)\n"); } else // アクションに指定無し。 { debug_log_output("HTTP joint file response start!\n"); http_joint_file_response(accept_socket, &http_recv_info); debug_log_output("HTTP joint file response end!\n"); } } else if ( result == PLW_UPL_TYPE ) { // --------------------------------------------- // plw/uplファイル(`・ω・´) // リストファイルから、プレイリスト生成して返信 // --------------------------------------------- debug_log_output("HTTP wizd play list create and response start!\n"); http_listfile_to_playlist_create(accept_socket, &http_recv_info); debug_log_output("HTTP wizd play list create and response end!\n"); } else if ( result == VOB_TYPE ) { // --------------------------------------------- // vobファイル 連結 // vobを連結して返信 // --------------------------------------------- // actionに、SinglePlayが指示されている? if ( strcasecmp(http_recv_info.action, "SinglePlay" ) == 0) { // ---------------------------------------- // Musicファイル 単独プレイ // ---------------------------------------- debug_log_output("Single Play start!(VOB)\n"); http_music_single_play(accept_socket, &http_recv_info); debug_log_output("Single Play end!(VOB)\n"); } else // アクションに指定無し。 { debug_log_output("HTTP vob file response start!\n"); http_vob_file_response(accept_socket, &http_recv_info); debug_log_output("HTTP vob file response end!\n"); } } else if ( result == CGI_TYPE ) { // --------------------------------------------- // cgiファイル // cgiを実行して結果を返信 // --------------------------------------------- debug_log_output("HTTP CGI response start!\n"); http_cgi_response(accept_socket, &http_recv_info); debug_log_output("HTTP CGI response end!\n"); } else if ( result == ISO_TYPE ) { // ISO file handling if ( (strcasecmp(http_recv_info.action, "IsoPlay" ) == 0) || (strcasecmp(http_recv_info.action, "dvdplay" ) == 0) || (strncmp(http_recv_info.action, "showchapters", 12 ) == 0)) { debug_log_output("ISO playlist create start!\n"); http_menu(accept_socket, &http_recv_info); debug_log_output("ISO playlist create end!\n"); } else { // Start to stream the VOB debug_log_output("HTTP ISO file response start!\n"); http_vob_file_response(accept_socket, &http_recv_info); debug_log_output("HTTP ISO file response end!\n"); } } else { // ---------------------------------------- // ディレクトリ // ---------------------------------------- // actionに、OptionMenuが指示されている? if ( strcasecmp(http_recv_info.action, "OptionMenu" ) == 0) { debug_log_output("HTTP Option menu create.\n"); http_option_menu(accept_socket, &http_recv_info); debug_log_output("HTTP Option menu end.\n"); } else if ( strcasecmp(http_recv_info.action, "copy" ) == 0) { // -------------------------------------------- // Copy the default playlist to this directory // -------------------------------------------- char source[WIZD_FILENAME_MAX]; char dest[WIZD_FILENAME_MAX]; int fd_source, fd_dest; if(http_recv_info.default_file_type == TYPE_MUSICLIST) { snprintf(dest, sizeof(dest), "%s/%s", http_recv_info.send_filename, DEFAULT_MUSICLIST); snprintf(source, sizeof(source), "%s/%s/%s", global_param.skin_root, global_param.skin_name, DEFAULT_MUSICLIST); } else { snprintf(dest, sizeof(dest), "%s/%s", http_recv_info.send_filename, DEFAULT_PHOTOLIST); snprintf(source, sizeof(source), "%s/%s/%s", global_param.skin_root, global_param.skin_name, DEFAULT_PHOTOLIST); } duplex_character_to_unique(source, '/'); duplex_character_to_unique(dest, '/'); debug_log_output("Copying '%s' to '%s'", source, dest); // Copy the file fd_source = open(source, O_RDONLY); if(fd_source >= 0) { fd_dest = open(dest, O_CREAT | O_TRUNC | O_WRONLY, S_IREAD | S_IWRITE); if(fd_dest >= 0) { while( (i = read(fd_source, source, sizeof(source))) > 0 ) { write(fd_dest, source, i); } close(fd_dest); } else { debug_log_output("Failed to open destination file"); } close(fd_source); } else { debug_log_output("Failed to open source file"); } // Redirect to the directory strncpy(dest, http_recv_info.recv_uri, sizeof(dest)); cut_after_last_character(dest, '/'); strcat(dest, "/"); http_redirect_response(accept_socket, &http_recv_info, dest); debug_log_output("Copy playlist end!\n"); } else // DIRECTORY_TYPE or {wizd,index}.htm{l} { int found = 0; if (global_param.flag_use_index) { if (http_find_and_replace(accept_socket, &http_recv_info)) found = 1; } if (!found) { debug_log_output("HTTP file menu create.\n"); http_menu(accept_socket, &http_recv_info); debug_log_output("HTTP file menu end.\n"); } } } // Clean shutdown shutdown(accept_socket, SHUT_WR); for(i=0; i<1 && (read(accept_socket, &result, sizeof(result))>0); i++); // ソケットクローズ close(accept_socket); return; }
// ************************************************************************** // JOINTファイルを、連結して返信する。 // ************************************************************************** int http_joint_file_response(int accept_socket, HTTP_RECV_INFO *http_recv_info_p ) { int send_header_data_len; int result_len; unsigned char send_http_header_buf[2048]; unsigned char work_buf[1024]; off_t joint_content_length = 0; JOINT_FILE_INFO_T *joint_file_info_p; int ret; int i; debug_log_output("---------------------------------------------------"); debug_log_output("http_joint_file_response() start" ); // ----------------------- // 作業用変数初期化 // ----------------------- memset(send_http_header_buf, '\0', sizeof(send_http_header_buf) ); // ----------------------- // ワーク領域確保 // ----------------------- joint_file_info_p = malloc( sizeof(JOINT_FILE_INFO_T) ); if ( joint_file_info_p == NULL ) { debug_log_output("malloc() error."); return ( -1 ); } // -------------------------- // SVIファイル情報をGET // -------------------------- ret = analyze_svi_file(http_recv_info_p->send_filename, joint_file_info_p ); debug_log_output("http_joint_file_response() end (ret=%d)" ); if ( ret >= 0 ) { debug_log_output("joint_file_info.file_num = %d", joint_file_info_p->file_num); debug_log_output("joint_file_info.total_size = %d", joint_file_info_p->total_size); for ( i = 0; i< joint_file_info_p->file_num; i++ ) { debug_log_output("[%02d] '%s' %d\n", i, joint_file_info_p->file[i].name, joint_file_info_p->file[i].size ); } // 送信するcontnet_length 計算 if ( http_recv_info_p->range_end_pos > 0 ) { joint_content_length = (http_recv_info_p->range_end_pos - http_recv_info_p->range_start_pos) + 1; } else { joint_content_length = joint_file_info_p->total_size - http_recv_info_p->range_start_pos; } } // ------------------------- // HTTP_OK ヘッダ生成 // ------------------------- strncpy(send_http_header_buf, HTTP_OK, sizeof(send_http_header_buf)); strncat(send_http_header_buf, HTTP_CONNECTION, sizeof(send_http_header_buf) - strlen(send_http_header_buf) ); snprintf(work_buf, sizeof(work_buf), HTTP_SERVER_NAME, SERVER_NAME); strncat(send_http_header_buf, work_buf, sizeof(send_http_header_buf) - strlen(send_http_header_buf) ); snprintf(work_buf, sizeof(work_buf), HTTP_CONTENT_LENGTH, joint_content_length); strncat(send_http_header_buf, work_buf, sizeof(send_http_header_buf) - strlen(send_http_header_buf) ); snprintf(work_buf, sizeof(work_buf), HTTP_CONTENT_TYPE, http_recv_info_p->mime_type); strncat(send_http_header_buf, work_buf, sizeof(send_http_header_buf) - strlen(send_http_header_buf) ); strncat(send_http_header_buf, HTTP_END, sizeof(send_http_header_buf) - strlen(send_http_header_buf) ); send_header_data_len = strlen(send_http_header_buf); debug_log_output("send_header_data_len = %d\n", send_header_data_len); debug_log_output("--------\n"); debug_log_output("%s", send_http_header_buf); debug_log_output("--------\n"); // -------------- // ヘッダ返信 // -------------- result_len = send(accept_socket, send_http_header_buf, send_header_data_len, 0); debug_log_output("result_len=%d, send_header_data_len=%d\n", result_len, send_header_data_len); // -------------- // 実体返信 // -------------- http_joint_file_send( accept_socket, joint_file_info_p, joint_content_length, http_recv_info_p->range_start_pos ); free( joint_file_info_p ); // Memory Free. return 0; }
off_t copy_FILE_to_descriptor(FILE *in_fp, int out_fd, off_t content_length) { unsigned char *p; int n; int len; int count; int buflen; off_t written = 0; off_t remain = content_length; if(global_param.stream_chunk_size < 512) global_param.stream_chunk_size = 512; // ====================== // 送信バッファを確保 // ====================== buflen = global_param.stream_chunk_size; p = malloc(buflen); if ( p == NULL ) { debug_log_output("malloc() error.\n"); return ( 0 ); } debug_log_output("Sending %lld bytes", content_length); // If we have no size specified, send an "infinite" number of bytes, or until EOF if(remain == 0) remain = (off_t)(-1); // ================ // 実体転送開始 // ================ while ( remain ) { // Read a block from the file count = fread(p, 1, (remain > buflen) ? buflen : remain, in_fp); if(count > 0) { remain -= count; // Write this data to the socket for(n=0; n < count; n += len) { len = write(out_fd, p+n, ((count-n) < global_param.socket_chunk_size) ? (count-n) : global_param.socket_chunk_size); if(len <= 0) { // Failed to write - end the stream remain = 0; if(len < 0) { debug_log_output("write failed. err = %s", strerror(errno)); } else { debug_log_output("socket closed by player"); } break; } else { written += len; } } } else { if(count < 0) { debug_log_output("read failed. err = %s", strerror(errno)); } else { debug_log_output("EOF"); // Open the next file in the sequence... } remain = 0; } } debug_log_output("Wrote %llu bytes", written); free(p); // 正常終了 return written; }
// ************************************************************************** // リクエストされたURIのファイルをチェック // documet_rootと、skin置き場をセットで探す。 // // Sets http_recv_info_p->file_type to one of the following: // -2: Directory without the slash // -1: File not found // 0: Normal file // 1: Directory // 2: SVI // 3: plw/upl // 4: tsv // 5: VOB video file // 6: CGI script // 7: ISO DVD image // ************************************************************************** static int http_file_check( HTTP_RECV_INFO *http_recv_info_p) { struct stat send_filestat; int result; int len; unsigned char file_extension[16]; debug_log_output("http_file_check() start."); // --------------- // 作業用変数初期化 // --------------- memset(http_recv_info_p->send_filename, '\0', sizeof(http_recv_info_p->send_filename)); memset(file_extension, '\0', sizeof(file_extension)); // ------------------------- // ファイルチェック // ------------------------- // 要求パスのフルパス生成。 strncpy(http_recv_info_p->send_filename, global_param.document_root, sizeof(http_recv_info_p->send_filename)); strncat(http_recv_info_p->send_filename, http_recv_info_p->recv_uri, sizeof(http_recv_info_p->send_filename)); // Check if the URI is an alias // printf("uri1 is %s\n", http_recv_info_p->recv_uri); // printf("send_filename1 %s\n", http_recv_info_p->send_filename); if(global_param.num_aliases && (http_recv_info_p->recv_uri[0] == '/')) { int i,j,len, skip_stat; char *p; for(i=0; i<global_param.num_aliases; i++) { len = strlen(global_param.alias_name[i]); if ((strncmp(http_recv_info_p->recv_uri + 1, global_param.alias_name[i], len) == 0) && (http_recv_info_p->recv_uri[len+1] == '/') ) { // see code in set_thumb_file in wizd_menu.c p = strrchr(http_recv_info_p->recv_uri, '.'); if (p && isdigit(p[1]) && isdigit(p[2]) && !p[3]) { p[0] = 0; p++; j = atoi(p); skip_stat = 1; } else { j = i; skip_stat = 0; } debug_log_output("substituting alias path '%s' for '%s'\n", global_param.alias_path[j], global_param.alias_name[j]); // The name matches an alias - substitute the alias path strncpy(http_recv_info_p->send_filename, global_param.alias_path[j], sizeof(http_recv_info_p->send_filename)); strncat(http_recv_info_p->send_filename, http_recv_info_p->recv_uri+len+1, sizeof(http_recv_info_p->send_filename)-strlen(http_recv_info_p->send_filename)); if (!skip_stat && stat(http_recv_info_p->send_filename, &send_filestat) != 0) continue; // Set the default allplay type, if it was not specified in the http request if(http_recv_info_p->default_file_type == TYPE_UNKNOWN) { http_recv_info_p->default_file_type = global_param.alias_default_file_type[j]; debug_log_output("Setting default file type to %d\n",http_recv_info_p->default_file_type); } else { debug_log_output("Leaving default file type set at %d\n",http_recv_info_p->default_file_type); } break; } } } // fix up shortcuts # ifdef HAVE_W32API len = strlen(http_recv_info_p->recv_uri); if (len > 4 && strcmp(&http_recv_info_p->recv_uri[len - 4], ".lnk") == 0) { char retPath1[256]; char retPath2[256]; char *p1, *p2; cygwin_conv_to_full_win32_path(http_recv_info_p->send_filename, retPath1); if (get_target(retPath1, retPath2) == 0) { cygwin_conv_to_full_posix_path(retPath2, http_recv_info_p->send_filename); p1 = strrchr(http_recv_info_p->recv_uri, '/'); p1++; p2 = strrchr(http_recv_info_p->send_filename, '/'); p2++; strcpy(p1, p2); } } # endif // printf("send_filename2 %s\n", http_recv_info_p->send_filename); // printf("uri2 is %s\n\n", http_recv_info_p->recv_uri); // '/' が重なってるところの重複を排除。 duplex_character_to_unique(http_recv_info_p->send_filename, '/'); debug_log_output("http_recv_info_p->send_filename='%s'\n", http_recv_info_p->send_filename); // Return "File not found" unless we get a match http_recv_info_p->file_type = -1; // ------------------------------------------------------------ // ファイルあるかチェック。 // ------------------------------------------------------------ result = stat(http_recv_info_p->send_filename, &send_filestat); debug_log_output("stat: result=%d, st_mode=0x%04X, S_ISREG=%d, S_ISDIR=%d size=%lld\n", result, send_filestat.st_mode, S_ISREG(send_filestat.st_mode), S_ISDIR(send_filestat.st_mode), send_filestat.st_size ); http_recv_info_p->file_size = send_filestat.st_size; if(result < 0) { debug_log_output("stat() error\n", result); // ---------------------------------------------------------------------------- // もし、実体が存在しなかったら、skin置き場に変えてもう一度チェック // Skin置き場は実体ファイルのみ認める。 // ---------------------------------------------------------------------------- debug_log_output("DocumentRoot not found. SkinDir Check."); debug_log_output("global_param.skin_root='%s'", global_param.skin_root); debug_log_output("global_param.skin_name='%s'", global_param.skin_name); // skin置き場にあるモノとして、フルパス生成。 strncpy(http_recv_info_p->send_filename, global_param.skin_root, sizeof(http_recv_info_p->send_filename)); strncat(http_recv_info_p->send_filename, global_param.skin_name, sizeof(http_recv_info_p->send_filename)); strncat(http_recv_info_p->send_filename, http_recv_info_p->recv_uri, sizeof(http_recv_info_p->send_filename)); // '/' が重なってるところの重複を排除。 duplex_character_to_unique(http_recv_info_p->send_filename, '/'); debug_log_output("SkinDir:http_recv_info_p->send_filename='%s'\n", http_recv_info_p->send_filename); // ------------------------------------------------------------ // Skin置き場にファイルあるかチェック。 // ------------------------------------------------------------ result = stat(http_recv_info_p->send_filename, &send_filestat); debug_log_output("stat: result=%d, st_mode=%04X, S_ISREG=%d\n", result, send_filestat.st_mode, S_ISREG(send_filestat.st_mode)); if( result < 0 ) { // Strip off the path, and try just the filename in the skin directory unsigned char *ptr = strrchr(http_recv_info_p->recv_uri, '/'); if((ptr != NULL) && (ptr != http_recv_info_p->recv_uri)) { strncpy(http_recv_info_p->send_filename, global_param.skin_root, sizeof(http_recv_info_p->send_filename)); strncat(http_recv_info_p->send_filename, global_param.skin_name, sizeof(http_recv_info_p->send_filename)-strlen(http_recv_info_p->send_filename)); strncat(http_recv_info_p->send_filename, ptr, sizeof(http_recv_info_p->send_filename)-strlen(http_recv_info_p->send_filename)); duplex_character_to_unique(http_recv_info_p->send_filename, '/'); debug_log_output("SkinDir:http_recv_info_p->send_filename='%s'\n", http_recv_info_p->send_filename); result = stat(http_recv_info_p->send_filename, &send_filestat); debug_log_output("stat: result=%d, st_mode=%04X, S_ISREG=%d\n", result, send_filestat.st_mode, S_ISREG(send_filestat.st_mode)); } } // ファイル実体と検知。 if ( ( result < 0 ) || !(S_ISREG(send_filestat.st_mode)) ) { // ------------------------------------- // File Not Found. // やっぱり、404にしよう。 // ------------------------------------- http_recv_info_p->file_type = -1; // Not found return(http_recv_info_p->file_type); } } // stat()の結果で分岐。 if ( ( result == 0 ) && ( S_ISREG(send_filestat.st_mode) == 1 ) ) { // ファイル実体と検知 debug_log_output("'%s' is File!!", http_recv_info_p->send_filename); debug_log_output("send_filestat.st_size= %lld\n", send_filestat.st_size); // Hack to overcome Linkplayer bug - it limits bookmark offset to (2^31)-1 // so we remember the last bookmark served so we can recover the real offset if((http_recv_info_p->range_start_pos == 2147483647) && global_param.bookmark_threshold) { // Check for the real bookmark FILE *fp; off_t offset; char work_buf[WIZD_FILENAME_MAX]; snprintf(work_buf, sizeof(work_buf), "%s.wizd.bookmark", http_recv_info_p->send_filename); debug_log_output("Double-checking bookmark: '%s'", work_buf); fp = fopen(work_buf, "r"); if(fp != NULL) { fgets(work_buf, sizeof(work_buf), fp); fclose(fp); offset = atoll(work_buf); if(offset > http_recv_info_p->range_start_pos) { debug_log_output("Detected LinkPlayer2 bookmark bug, replacing 'Range: %lld-' with 'Range: %lld-'", http_recv_info_p->range_start_pos, offset); http_recv_info_p->range_start_pos = offset; } } } // ------------------------------------------- // ファイルの拡張子より、Content-type を決定 // ------------------------------------------- filename_to_extension(http_recv_info_p->send_filename, file_extension, sizeof(file_extension)); debug_log_output("http_recv_info_p->send_filename='%s', file_extension='%s'\n", http_recv_info_p->send_filename, file_extension); // 拡張子から、mime_typeを導く。 http_recv_info_p->menu_file_type = check_file_extension_to_mime_type(file_extension, http_recv_info_p->mime_type, sizeof(http_recv_info_p->mime_type)); // SVIファイルと実体ファイルで分岐 if (( strcasecmp(file_extension, "svi") == 0 ) || ( strcasecmp(file_extension, "sv3") == 0 )) { http_recv_info_p->file_type = SVI_TYPE; // sviファイル } else if ( ( http_recv_info_p->menu_file_type == TYPE_PLAYLIST ) || ( http_recv_info_p->menu_file_type == TYPE_MUSICLIST )) /*( strcasecmp(file_extension, "plw") == 0 ) || ( strcasecmp(file_extension, "pls") == 0 ) || ( strcasecmp(file_extension, "m3u") == 0 ) || ( strcasecmp(file_extension, "upl") == 0 ) )*/ { http_recv_info_p->file_type = PLW_UPL_TYPE; // plw/upl ファイル } else if (( strcasecmp(file_extension, "vob") == 0 ) || ( strcasecmp(file_extension, "ts") == 0 ) || ( strcasecmp(file_extension, "tp") == 0 ) ) { // VOB_TYPE files combine files with increasing digits at the end of the filename // into one large virtual file http_recv_info_p->file_type = VOB_TYPE; // vobファイル } else if ( strcasecmp(file_extension, "tsv") == 0 ) { http_recv_info_p->file_type = TSV_TYPE; // tsvファイル } else if ( strcasecmp(file_extension, "cgi") == 0 ) { // CGIの実行が不許可なら、Not Found. http_recv_info_p->file_type = (global_param.flag_execute_cgi ? CGI_TYPE : -1 ); // cgiファイル } else if ( http_recv_info_p->menu_file_type == TYPE_ISO ) { http_recv_info_p->file_type = ISO_TYPE; } else { http_recv_info_p->file_type = GENERAL_FILE_TYPE; // File実体 } } else if ( ( result == 0 ) && ( S_ISDIR(send_filestat.st_mode) == 1 ) ) // ディレクトリ { int len; len = strlen(http_recv_info_p->recv_uri); if (len > 0 && http_recv_info_p->recv_uri[len - 1] != '/') { // '/' で終端していないディレクトリ要求の場合... http_recv_info_p->file_type = -2; } // ディレクトリと検知 debug_log_output("'%s' is Dir!!", http_recv_info_p->send_filename); http_recv_info_p->file_type = DIRECTORY_TYPE ; // ディレクトリ } return( http_recv_info_p->file_type ); }
int http_proxy_response(int accept_socket, HTTP_RECV_INFO *http_recv_info_p) { const char *HTTP_RECV_CONTENT_TYPE = "Content-type: text/html"; const char *HTTP_RECV_CONTENT_LENGTH = "Content-Length: "; const char *HTTP_RECV_LOCATION = "Location: "; char *p_target_host_name; char *p_uri_string; char send_http_header_buf[2048]; char line_buf[MAX_LINE][LINE_BUF_SIZE + 5]; char proxy_pre_string[2048]; char base_url[2048]; char *p_url; char *p; char *p_auth = NULL; int port = 80; int sock; off_t content_length = 0; int len = 0; int line = 0; int i; int content_is_html = 0; p_uri_string = http_recv_info_p->request_uri; if (!strncmp(p_uri_string, "/-.-playlist.pls?", 17)) { char buff[FILENAME_MAX]; if (p_uri_string[17] == '/') { snprintf(buff, sizeof(buff), "http://%s%s" , http_recv_info_p->recv_host, p_uri_string+17); } else { strncpy(buff, p_uri_string+17, sizeof(buff)); } replace_character((char*)buff, ".pls", ""); return 0;//http_uri_to_scplaylist_create(accept_socket, (char*)buff); } if (strncmp(p_uri_string, "/-.-http://", 11)) { return -1; } if (!global_param.flag_allow_proxy) { return -1; } strncpy(base_url, p_uri_string, 2048); p = strrchr(base_url, '/'); p[1] = '\0'; p_target_host_name = p_uri_string + 11; p_url = strchr(p_target_host_name, '/'); if (p_url == NULL) return -1; *p_url++ = '\0'; strncpy(proxy_pre_string, p_uri_string, 2048); p = strchr(p_target_host_name, '@'); if (p != NULL) { // there is a user name. p_auth = p_target_host_name; p_target_host_name = p + 1; *p = '\0'; } p = strchr(p_target_host_name, ':'); if (p != NULL) { port = atoi(p+1); *p = '\0'; } debug_log_output("proxy:target_host_name: %s", p_target_host_name); debug_log_output("proxy:authenticate: %s", p_auth ? p_auth : "NULL"); debug_log_output("proxy:url: %s", p_url); debug_log_output("proxy:prestring: %s", proxy_pre_string); debug_log_output("proxy:base_url: %s", base_url); debug_log_output("proxy:port: %d", port); p = send_http_header_buf; if( http_recv_info_p->isGet == 1 ){ p += sprintf((char*)p, "GET /%s HTTP/1.0\r\n", p_url); }else if ( http_recv_info_p->isGet == 2 ){ p += sprintf((char*)p, "HEAD /%s HTTP/1.0\r\n", p_url); } p += sprintf((char*)p, "Host: %s:%u\r\n", p_target_host_name, port); if (global_param.user_agent_proxy_override[0]) { p += sprintf((char*)p, "User-agent: %s\r\n", global_param.user_agent_proxy_override); } else { if (http_recv_info_p->user_agent[0]) { p += sprintf((char*)p, "User-agent: %s\r\n", http_recv_info_p->user_agent); } else { p += sprintf((char*)p, "User-agent: %s\r\n", SERVER_NAME); } } p += sprintf(p, "Accept: */*\r\n"); p += sprintf(p, "Connection: close\r\n"); if (http_recv_info_p->range_start_pos) { p += sprintf((char*)p, "Range: bytes="); p += sprintf((char*)p, "%zu-", http_recv_info_p->range_start_pos); if (http_recv_info_p->range_end_pos) { p += sprintf((char*)p, "%zu", http_recv_info_p->range_end_pos); } p += sprintf(p, "\r\n"); } if (p_auth != NULL) { p += sprintf((char*)p, "Authorization: Basic %s\r\n", base64((unsigned char*)p_auth)); } p += sprintf(p, "\r\n"); sock = sock_connect((char*)p_target_host_name, port); if (sock < 0) { debug_log_output("error: %s", strerror(errno)); debug_log_output("sock: %d", sock); return -1; } set_nonblocking_mode(sock, 0); /* blocking mode */ set_nonblocking_mode(accept_socket, 0); /* blocking mode */ write(sock, send_http_header_buf, strlen(send_http_header_buf)); debug_log_output("================= send to proxy\n"); debug_log_output("%s", send_http_header_buf); debug_log_output("=================\n"); //TODO ここが404や400の可能性あり for (line = 0; line < MAX_LINE; line ++) { char work_buf[LINE_BUF_SIZE + 10]; memset(line_buf[line], 0, LINE_BUF_SIZE + 5); len = line_receive(sock, line_buf[line], LINE_BUF_SIZE + 1); if (len < 0) break; debug_log_output("recv html: '%s' len = %d", ((len)?(char*)line_buf[line]:""), len); line_buf[line][len++] = '\r'; line_buf[line][len++] = '\n'; line_buf[line][len] = '\0'; if (!strncasecmp(line_buf[line], HTTP_RECV_CONTENT_TYPE, strlen(HTTP_RECV_CONTENT_TYPE))) { int n; content_is_html = 1; n = strlen(HTTP_RECV_CONTENT_TYPE); line_buf[line][n++] = '\r'; line_buf[line][n++] = '\n'; line_buf[line][n] = '\0'; }else if (!strncasecmp(line_buf[line], HTTP_RECV_CONTENT_LENGTH, strlen(HTTP_RECV_CONTENT_LENGTH))) { content_length = strtoull((char*)(line_buf[line] + strlen(HTTP_RECV_CONTENT_LENGTH)), NULL, 0); }else if (!strncasecmp(line_buf[line], HTTP_RECV_LOCATION, strlen(HTTP_RECV_LOCATION))) { strcpy(work_buf, line_buf[line]); sprintf((char*)line_buf[line], "Location: /-.-%s", work_buf + strlen(HTTP_RECV_LOCATION)); }else if ( strstr( line_buf[line],"charset") ){ if ( strstr( line_buf[line], "EUC-JP" )){ replace_character( (char*)line_buf[line], "EUC-JP", "UTF-8"); }else if ( strstr ( line_buf[line], "Shift_JIS" )) { replace_character( (char*)line_buf[line], "Shift_JIS", "UTF-8"); } } if (len <= 2) { line++; break; } } if (len < 0 ) { debug_log_output("CLOSED-C"); close(sock); return -1; } if (content_is_html) { char *p, *q; char *new_p, *r; char work_buf[LINE_BUF_SIZE + 10]; char work_buf2[LINE_BUF_SIZE * 2]; char *link_pattern = (char*)"<A HREF="; int flag_conv_html_code = 1; for (i=0; i<line; i++) { //長さは不定なので送らない if (!strncasecmp(line_buf[i], HTTP_RECV_CONTENT_LENGTH, strlen(HTTP_RECV_CONTENT_LENGTH))) continue; //*q = '\0'; p = work_buf; q = work_buf2; if (flag_conv_html_code) { convert_language_code(line_buf[i], work_buf, LINE_BUF_SIZE, CODE_AUTO, global_param.client_language_code); } //strcpy( work_buf, line_buf[i]); // write(accept_socket, line_buf[i], strlen(line_buf[i])); // debug_log_output("sent html: '%s' len = %d", line_buf[i], strlen(line_buf[i])); write(accept_socket, work_buf, strlen(line_buf[i])); debug_log_output("sent html: '%s' len = %d", work_buf, strlen(work_buf)); } debug_log_output("sent header"); //write(accept_socket, "debug:--\n", strlen("debug:--\n")); while (1) { char rep_str[1024]; memset(work_buf, 0, LINE_BUF_SIZE); len = line_receive(sock, work_buf, LINE_BUF_SIZE); if (len < 0) break; debug_log_output("recv html: '%s' len = %d", work_buf, len); work_buf[len++] = '\r'; work_buf[len++] = '\n'; work_buf[len] = '\0'; if (my_strcasestr(work_buf, "Content-Type") != NULL) { // Content-Type があったら // 漢字コードの変換をやめる // flag_conv_html_code = 0; if ( strstr( work_buf, "EUC-JP" )){ replace_character( (char*)work_buf, "EUC-JP", "UTF-8"); }else if ( strstr ( work_buf, "Shift_JIS" )) { replace_character( (char*)work_buf, "Shift_JIS", "UTF-8"); } } p = work_buf; q = work_buf2; memset(q, 0, sizeof(work_buf2)); /* * HTML中に <A HREF="..."> があったら、プロクシを経由するように * たとえば <A HREF="/-.-http://www.yahoo.co.jp/"> のように変換する * もし、ファイルがストリーム可能そうであれば、VOD="0" も追加する。 * * 問題点: * タグの途中に改行があると失敗するだろう. * 面倒なのでたいした置換はしていない * 惰性で書いたので汚い。だれか修正して。 */ link_pattern = (char*)"<A HREF="; while ((new_p = my_strcasestr(p, link_pattern)) != NULL) { int l = new_p - p + strlen(link_pattern); char *tmp; MIME_LIST_T *mlt = NULL; strncpy(q, p, l); q += l; p += l; /* i.e., p = new_p + strlen(link_pattern); */ r = strchr(p, '>'); if (r == NULL) continue; *r = '\0'; if (*p == '"') *q++ = *p++; if ((tmp = strchr(p, '"')) != NULL || (tmp = strchr(p, ' ')) != NULL) { mlt = mime_list; while (mlt->mime_name != NULL) { if (*(tmp - strlen(mlt->file_extension) - 1) == '.' && !strncasecmp(tmp - strlen(mlt->file_extension), mlt->file_extension, strlen(mlt->file_extension))) { break; } mlt++; } } //if (flag_pc && mlt != NULL && mlt->stream_type == TYPE_STREAM) { // q += sprintf((char*)q, "/-.-playlist.pls?http://%s", http_recv_info_p->recv_host); //} if (*p == '/') { q += sprintf(q, "%s%s", proxy_pre_string, p); } else if (!strncmp(p, "http://", 7)) { q += sprintf(q, "/-.-%s", p); } else { q += sprintf((char*)q, "%s%s", base_url, p); //q += sprintf((char*)q, "%s", p); } if (mlt != NULL && mlt->stream_type == TYPE_STREAM) { q += sprintf(q, " vod=\"0\""); } *q++ = '>'; p = r + 1; } while (*p) *q++ = *p++; *q = '\0'; /* * HTML中に SRC="..." があったら、プロクシを経由するように変換する * * 問題点: * タグの途中に改行があると失敗するだろう. * 変数使いまわしたので、融通が効かない。 * だれか修正して。 */ p = work_buf2; q = work_buf; memset(q, 0, sizeof(work_buf)); link_pattern = (char*)"SRC="; while ((new_p = my_strcasestr(p, link_pattern)) != NULL) { int l = new_p - p + strlen(link_pattern); strncpy(q, p, l); q += l; p += l; /* i.e., p = new_p + strlen(link_pattern); */ if (*p == '"') *q++ = *p++; if (*p == '/') { q += sprintf(q, "%s", proxy_pre_string); } else if (!strncmp(p, "http://", 7)) { q += sprintf(q, "/-.-"); } else { q += sprintf(q, "%s", base_url); //q += sprintf(q, "%s", p); } } while (*p) *q++ = *p++; *q = '\0'; p = work_buf; q = work_buf2; if (flag_conv_html_code) { convert_language_code(p, q, LINE_BUF_SIZE, CODE_AUTO, global_param.client_language_code); //strcpy( q,p); p = q; } snprintf(rep_str, sizeof(rep_str), "|http://%s/-.-http://" , http_recv_info_p->recv_host); replace_character((char*)p, "|http://", rep_str); write(accept_socket, p, strlen(p)); debug_log_output("sent html: %s", p); } } else { for (i=0; i<line; i++) { write(accept_socket, line_buf[i], strlen(line_buf[i])); } if( http_recv_info_p->isGet == 1 ){ return ( copy_descriptors(sock, accept_socket, (size_t)content_length) ); //NULL , //(char*)http_recv_info_p->recv_uri, // http_recv_info_p->range_start_pos)); } } sClose(sock); return 0; }
off_t copy_descriptors(int in_fd, int out_fd, off_t content_length, JOINT_FILE_INFO_T *joint_file_info_p) { int i; pthread_t id; pthread_attr_t attr; thread_param_type param; int index = 0; int next_index; int oneback,twoback,shortcount; off_t total_read_size = 0; int len; off_t read_cnt = 0; int flag_verbose = 0; // Set to 1 for debugging sessions, set to 0 for release int flag_send_asap = global_param.flag_buffer_send_asap; ssize_t blocks_read = 0; int offset = 0; if(global_param.buffer_size < 1) global_param.buffer_size = 1; if(global_param.stream_chunk_size < 512) global_param.stream_chunk_size = 512; if (joint_file_info_p) offset = joint_file_info_p->iso_seek; if(flag_verbose && global_param.flag_debug_log_output) { // Redirect streaming debug log to stderr debug_log_initialize(NULL); } // ====================== // 送信バッファを確保 // ====================== param.count = global_param.buffer_size; // Need at least 4 buffers for this to be reasonable if(param.count < 4) param.count = 4; param.length = global_param.stream_chunk_size; debug_log_output("Allocating %d buffers of %d bytes each\n", param.count, param.length); param.bytes = (int *)malloc(param.count * sizeof(int)); param.total = (off_t *)malloc(param.count * sizeof(off_t)); param.buffer = (char **)malloc(param.count * sizeof(char *)); param.mutex = (pthread_mutex_t *)malloc(param.count * sizeof(pthread_mutex_t)); if ( ( param.bytes == NULL ) || ( param.buffer == NULL ) || ( param.mutex == NULL ) ) { debug_log_output("malloc() error.\n"); return (-1 ); } for(i=0; i<param.count; i++) { param.bytes[i] = 0; param.buffer[i] = (char *)malloc(param.length); if(param.buffer[i] == NULL) { debug_log_output("malloc() error.\n"); return ( 0 ); } pthread_mutex_init(¶m.mutex[i], NULL); } // Launch a separate thread to send the data out param.fd = out_fd; param.flags = 0; param.written = 0; // Lock the next block in advance pthread_mutex_lock(¶m.mutex[index]); pthread_attr_init(&attr); // This doesn't seek to work well under Cygwin //#define USE_PTHREAD_PRIORITY #ifdef USE_PTHREAD_PRIORITY { int policy,retcode,curr,mn,mx; struct sched_param schp; memset(&schp, 0, sizeof(schp)); // Get the current settings pthread_getschedparam(pthread_self(), &policy, &schp); curr=schp.sched_priority; mn=sched_get_priority_min(policy); mx=sched_get_priority_max(policy); schp.sched_priority = (curr+mx)/2; debug_log_output("Setting policy %d, priority %d (was=%d, min=%d, max=%d)\n", policy, schp.sched_priority, curr, mn, mx); retcode = pthread_attr_setschedpolicy(&attr,policy); if(retcode != 0) { debug_log_output("pthread_attr_setschedpolicy returned %d\n",retcode); } retcode = pthread_attr_setschedparam(&attr,&schp); if(retcode != 0) { debug_log_output("pthread_attr_setschedparam returned %d\n",retcode); } } #endif if((i = pthread_create(&id, &attr, send_buffers, ¶m)) != 0) { debug_log_output("pthread_create returned %d\n",i); return ( 0 ); } // Watch the two bins behind, and shorten the buffer if the write thread is catching up twoback = param.count - 2; oneback = param.count - 1; shortcount = param.length/4; if(shortcount < 4096) shortcount = 4096; while ( param.flags == 0 ) { // Wait for a buffer to free up next_index = index+1; if(next_index >= param.count) next_index = 0; if(pthread_mutex_lock(¶m.mutex[next_index]) == 0) { for(i=0 ; i < param.length; ) { len = param.length - i; if(len > global_param.file_chunk_size) len = global_param.file_chunk_size; if ((joint_file_info_p!=NULL) && (joint_file_info_p->dvd_file != NULL)) { blocks_read = DVDReadBlocks(joint_file_info_p->dvd_file, offset, len/2048, param.buffer[index] + i); len = (off_t)blocks_read*2048; offset += blocks_read; } else if (joint_file_info_p && read_cnt >= joint_file_info_p->file[joint_file_info_p->current_file_num].size) { len = 0; read_cnt = 0; debug_log_output("finished file chunk %d\n", joint_file_info_p->current_file_num); //printf("finished file chunk %d\n", joint_file_info_p->current_file_num); } else { if (joint_file_info_p && len + read_cnt > joint_file_info_p->file[joint_file_info_p->current_file_num].size) { len = joint_file_info_p->file[joint_file_info_p->current_file_num].size - read_cnt; debug_log_output("finishing last block of %d\n", joint_file_info_p->current_file_num); //printf("finishing last block of %d\n", joint_file_info_p->current_file_num); } len = read(in_fd, param.buffer[index] + i, len); read_cnt += len; } if(len == 0) { // End of file if( (joint_file_info_p==NULL) || (joint_file_info_p->dvd_file != NULL) || next_file(&in_fd, joint_file_info_p)) { // No more files if (flag_verbose) { debug_log_output("recv: len = %d, idx = %d finish!", len, index); } else { debug_log_output( "*********************************************************\n" "** RECV FINISHED! **\n" "*********************************************************"); } param.flags |= FLAG_READ_COMPLETE; break; } else { // We opened up a new file, so continue the loop } } else if (len > 0) { i += len; total_read_size += len; if((content_length > 0) && (total_read_size >= content_length)) { // Only send as much as is requested if(total_read_size > content_length) { debug_log_output("Restricting content length, read=%lld, allowed=%lld", total_read_size, content_length); total_read_size -= content_length; i -= (int)total_read_size;; total_read_size = content_length; } param.flags |= FLAG_READ_COMPLETE; break; } } else { // Read error if (flag_verbose) { debug_log_output("read err?: len = %d, idx = %d, err: %s", len, index, strerror(errno)); } else { debug_log_output( "*********************************************************\n" "** RECV FINISHED!(ret = %d) **\n" "*********************************************************", len); } param.flags |= FLAG_READ_COMPLETE; break; } // Check the trailing bins to see if the write thread is catching us // with the exception that if we already fell behind, // then we allow it to rebuffer completely for one block if((i >= shortcount) && (param.bytes[twoback] == 0) && (total_read_size > param.length)) { if(param.bytes[oneback] == 0) { //fputc('.',stderr); // Only one buffer full - reloading } else { //fputc(':',stderr); // Only two buffers full, cut this buffer short break; } } } // Flag this block as ready-to-write param.bytes[index] = i; param.total[index] = total_read_size; if(flag_send_asap) { pthread_mutex_unlock(¶m.mutex[index]); } else if(next_index == (param.count-2)) { // Buffers are filled, so release the sending thread flag_send_asap = TRUE; for(i=0; i<next_index; i++) pthread_mutex_unlock(¶m.mutex[i]); } twoback = oneback; oneback = index; index = next_index; } } // Count the number of bytes in the queue for(i=0,len=0; i<param.count; i++) { len += param.bytes[i]; } debug_log_output("Read complete, read %lld bytes, %d bytes to transmit", total_read_size, len); // If we haven't released the sending thread yet, do so now if(!flag_send_asap) { for(i=0; i<index; i++) pthread_mutex_unlock(¶m.mutex[i]); } // Release the last block, unused pthread_mutex_unlock(¶m.mutex[index]); // Wait for the created thread to complete pthread_join(id,NULL); // Free up the allocated memory if(param.bytes != NULL) free(param.bytes); for(i=0; i<param.count; i++) { if(param.buffer[i] != NULL) free(param.buffer[i]); pthread_mutex_destroy(¶m.mutex[i]); } debug_log_output("copy descriptors end."); return param.written; }
// ************************************************************************** // ファイル実体の返信。 // ヘッダ生成&送信準備 // ************************************************************************** int http_file_response(int accept_socket, HTTP_RECV_INFO *http_recv_info_p) { int send_header_data_len; int result_len; unsigned char send_http_header_buf[2048]; unsigned char work_buf[1024]; off_t content_length; off_t written; off_t offset; struct stat file_stat; int result; int mpeg2video = (strstr(http_recv_info_p->mime_type, "video/mpeg") != NULL); // --------------- // 作業用変数初期化 // --------------- memset(send_http_header_buf, '\0', sizeof(send_http_header_buf)); /* if ( http_recv_info_p->range_start_pos == 0 ) { content_length = 0; } */ result = stat(http_recv_info_p->send_filename, &file_stat); // ファイルサイズチェック。 if ( result != 0 ) { debug_log_output("file not found."); return ( -1 ); } // Set the starting offset based on the requested page offset = (file_stat.st_size/10) * http_recv_info_p->page; if( global_param.bookmark_threshold && (http_recv_info_p->file_size > global_param.bookmark_threshold) && (http_recv_info_p->range_start_pos == 0)) { // Check for a bookmark FILE *fp; int previous_page = 0; off_t previous_size = 0; off_t bookmark = 0; snprintf(work_buf, sizeof(work_buf), "%s.wizd.bookmark", http_recv_info_p->send_filename); debug_log_output("Checking for bookmark: '%s'", work_buf); fp = fopen(work_buf, "r"); if(fp != NULL) { fgets(work_buf, sizeof(work_buf), fp); bookmark = atoll(work_buf); if(fgets(work_buf, sizeof(work_buf), fp) != NULL) { previous_size = atoll(work_buf); if(fgets(work_buf, sizeof(work_buf), fp) != NULL) { previous_page = atoi(work_buf); } } fclose(fp); debug_log_output("Bookmark offset: %lld/%lld (page %d)", bookmark, previous_size, previous_page); debug_log_output("Requested range %lld-%lld", http_recv_info_p->range_start_pos, http_recv_info_p->range_end_pos); } // Compare the current request with the stored bookmark // If requesting the same chapter as before, then don't adjust anything // If reqesting an earlier chapter, then go there directly if(http_recv_info_p->page < previous_page) { // Make sure we never advance the position on a previous-chapter command if((bookmark > 0) && (offset > bookmark)) { debug_log_output("Forcing offset from %lld to %lld to prevent advance when going to previous chapter", offset, bookmark); offset = bookmark; } else { debug_log_output("Going to previous chapter - no bookmark adjustment needed"); } } else if(http_recv_info_p->page > previous_page) { // If the file size is increasing, and bookmark is equal to the previous end-of-file // then adjust things so it continues playing where it left off // This will allow for (almost) continuous playback of files of increasing size if((previous_size > 0) && (file_stat.st_size > previous_size) && (offset < bookmark)) { debug_log_output("Forcing offset from %lld to %lld to get continuous playback", offset, bookmark); offset = bookmark; } else { // If requesting a following chapter, then return content_length=1 until we are beyond the bookmark location // (Note: I tried content_length=0, but this seems to really confuse the LinkPlayer!!!) if((global_param.dummy_chapter_length > 0) && (offset < bookmark)) { content_length = global_param.dummy_chapter_length; debug_log_output("Returning length=%lld because offset < bookmark (%lld < %lld)", content_length, offset, bookmark); http_recv_info_p->range_start_pos = offset; http_recv_info_p->range_end_pos = offset + content_length - 1; offset=0; // I also tried sending a not-found, but this confuses it too!!! /* snprintf(send_http_header_buf, sizeof(send_http_header_buf), HTTP_NOT_FOUND HTTP_CONNECTION HTTP_CONTENT_TYPE HTTP_END, "video/mpeg" ); send_header_data_len = strlen(send_http_header_buf); debug_log_output("send_header_data_len = %d\n", send_header_data_len); debug_log_output("--------\n"); debug_log_output("%s", send_http_header_buf); debug_log_output("--------\n"); result_len = send(accept_socket, send_http_header_buf, send_header_data_len, 0); debug_log_output("result_len=%d, send_data_len=%d\n", result_len, send_header_data_len); */ // I also tried simply closing the socket and exiting, but this confuses it too!!! //close(accept_socket); //return 0; } else if(offset > bookmark) { debug_log_output("Advancing to chapter %d, offset=%lld, bookmark=%lld", http_recv_info_p->page, offset, bookmark); } } } else if(previous_size > 0) { // When requesting the same chapter, then compute the offset using the previously saved length // so that fast-forward/rewind isn't affected by changing file sizes offset = (previous_size/10) * http_recv_info_p->page; debug_log_output("Forcing offset=%lld based on previous size (%lld) instead of current size (%lld)", offset, previous_size, file_stat.st_size); } } if(offset > 0) { debug_log_output("Offsetting start/end position by %lld", offset); http_recv_info_p->range_start_pos += offset; if(http_recv_info_p->range_end_pos > 0) http_recv_info_p->range_end_pos += offset; } if ( http_recv_info_p->range_end_pos > 0 ) // end位置指定有り。 { content_length = (http_recv_info_p->range_end_pos - http_recv_info_p->range_start_pos) + 1; } else { content_length = file_stat.st_size - http_recv_info_p->range_start_pos; } // -------------- // OK ヘッダ生成 // -------------- strncpy(send_http_header_buf, HTTP_OK, sizeof(send_http_header_buf)); strncat(send_http_header_buf, HTTP_CONNECTION, sizeof(send_http_header_buf) - strlen(send_http_header_buf)); snprintf(work_buf, sizeof(work_buf), HTTP_SERVER_NAME, SERVER_NAME); strncat(send_http_header_buf, work_buf, sizeof(send_http_header_buf) - strlen(send_http_header_buf)); if (content_length) { snprintf(work_buf, sizeof(work_buf), HTTP_CONTENT_LENGTH, content_length); strncat(send_http_header_buf, work_buf, sizeof(send_http_header_buf) - strlen(send_http_header_buf) ); } snprintf(work_buf, sizeof(work_buf), HTTP_CONTENT_TYPE, http_recv_info_p->mime_type); strncat(send_http_header_buf, work_buf, sizeof(send_http_header_buf) - strlen(send_http_header_buf) ); strncat(send_http_header_buf, HTTP_END, sizeof(send_http_header_buf) - strlen(send_http_header_buf) ); send_header_data_len = strlen(send_http_header_buf); debug_log_output("send_header_data_len = %d\n", send_header_data_len); debug_log_output("--------\n"); debug_log_output("%s", send_http_header_buf); debug_log_output("--------\n"); // -------------- // ヘッダ返信 // -------------- result_len = send(accept_socket, send_http_header_buf, send_header_data_len, 0); debug_log_output("result_len=%d, send_data_len=%d\n", result_len, send_header_data_len); // Small files go to simple function if((content_length > 0) && (content_length <= SEND_BUFFER_SIZE)) { written = http_simple_file_send( accept_socket, http_recv_info_p->send_filename, content_length, http_recv_info_p->range_start_pos ); } else { // -------------- // 実体返信 // -------------- written = http_file_send( accept_socket, http_recv_info_p->send_filename, content_length, http_recv_info_p->range_start_pos ); } if(global_param.bookmark_threshold && (mpeg2video || !global_param.flag_bookmarks_only_for_mpeg)) { /* if( (written < global_param.bookmark_threshold) // Less than 10 MB written || (content_length && ((written+global_param.bookmark_threshold) > content_length)) //within 10 MB of the end of file || (0 == strcmp(http_recv_info_p->mime_type, "video/mpeg")) ) { // Only create bookmarks for MPEG video files // Remove any existing bookmark snprintf(work_buf, sizeof(work_buf), "%s.wizd.bookmark", http_recv_info_p->send_filename); if(0 == unlink(work_buf)) { debug_log_output("Removed bookmark: '%s'", work_buf); } } else */ if(written > global_param.bookmark_threshold) { // Save a bookmark snprintf(work_buf, sizeof(work_buf), "%s.wizd.bookmark", http_recv_info_p->send_filename); written += http_recv_info_p->range_start_pos; // Back up to account for network buffering in the player // (don't back up if the bookmark is at the end of the file) if(written < file_stat.st_size) written -= global_param.bookmark_threshold; if(written > 0) { // always true here! FILE *fp = fopen(work_buf, "w"); if(fp != NULL) { // Write the bookmark location, the size (as we saw it), and the requested chapter number // which will be used to decide what to do when we receive the next request fprintf(fp, "%lld\n", written); fprintf(fp, "%lld\n", file_stat.st_size); fprintf(fp, "%d\n", http_recv_info_p->page); fclose(fp); debug_log_output("Wrote bookmark: '%s' page %d %lld of %lld", work_buf, http_recv_info_p->page, written, file_stat.st_size); } else { debug_log_output("create bookmark failed. err = %s", strerror(errno)); } } else { // Remove any old bookmarks if(0 == unlink(work_buf)) { debug_log_output("Removed bookmark: '%s'", work_buf); } } } } return 0; }
void *send_buffers(void *p) { thread_param_type *param = (thread_param_type *)p; int last_index = param->count-1; int index=0; int i,len; int flag_verbose = 0; // Set to 1 for debugging sessions, set to 0 for release if((param == NULL) || (param->count <= 0) || (param->length <= 0)) return NULL; // Lock the last block to prevent overrun pthread_mutex_lock(¶m->mutex[last_index]); debug_log_output("Threaded send started"); while((param->flags & (FLAG_ABORT|FLAG_WRITE_COMPLETE)) == 0) { if(pthread_mutex_lock(¶m->mutex[index])==0) { if(param->bytes[index] > 0) { if(flag_verbose && (param->flags & FLAG_READ_COMPLETE)) { debug_log_output("Sending block %d with %d bytes\n", index, param->bytes[index]); } // We have a buffer with data - send it out for(i=0; i<param->bytes[index]; ) { len = param->bytes[index] - i; if(len > global_param.socket_chunk_size) len = global_param.socket_chunk_size; len = send(param->fd, param->buffer[index] + i, len, 0); // Free up the mutex after we complete a send, so file read is triggered // after we have fresh data going out the network if(last_index >= 0) { pthread_mutex_unlock(¶m->mutex[last_index]); last_index=-1; } if(len <= 0) { // Failed to write - end the stream if (len < 0) { debug_log_output("send failed. err = %s", strerror(errno)); } else { debug_log_output("socket closed by player"); } param->flags |= FLAG_ABORT; break; } else { i += len; param->written += len; } } if(param->written != param->total[index]) { debug_log_output("Read/send mismatch, read=%lld, sent=%lld, buffer=%d, sent=%d", param->total[index], param->written, param->bytes[index], i); } } else if((param->flags & FLAG_READ_COMPLETE) == FLAG_READ_COMPLETE) { // No more data - so flag that we are finished sending param->flags |= FLAG_WRITE_COMPLETE; } else if(flag_verbose) { debug_log_output("ERROR: Got a zero-length block without a read-complete flag!!!"); } // Flag this block as complete param->bytes[index] = 0; // Free up the previous block if it hasn't been freed yet if(last_index >= 0) { pthread_mutex_unlock(¶m->mutex[last_index]); last_index=-1; } last_index=index; // Release this block only after we acquire the next if(++index >= param->count) index = 0; } else { debug_log_output("Failed to lock mutex %d, aborting!!!", index); param->flags |= FLAG_ABORT; } } // Release the last block pthread_mutex_unlock(¶m->mutex[last_index]); // Count the number of bytes in the queue for(i=0,len=0; i<param->count; i++) { len += param->bytes[i]; } debug_log_output("Send complete, sent %lld bytes, %d bytes left in the buffer", param->written, len); return NULL; }
// ************************************************************************** // SVI録画情報 読み込み // ************************************************************************** int read_svi_info(unsigned char *svi_filename, unsigned char *svi_info, int svi_info_size, unsigned int *rec_time ) { int fd; off_t lseek_ret; ssize_t read_length; size_t read_size; unsigned char rec_time_work[2]; debug_log_output("\n\nread_svi_info() start."); read_size = svi_info_size; if ( read_size > SVI_INFO_LENGTH ) { read_size = SVI_INFO_LENGTH; } memset( svi_info, '\0', svi_info_size ); *rec_time = 0; // -------------------------------- // SVIファイルから情報取り出す // -------------------------------- // SVIファイル開く fd = open(svi_filename, O_RDONLY); if ( fd < 0 ) { debug_log_output("oepn('%s') error.", svi_filename); return (-1); } // SVIファイルのファイル情報の位置にSEEK lseek_ret = lseek(fd, SVI_INFO_OFFSET, SEEK_SET); if ( lseek_ret < 0 ) { debug_log_output("lseek() error."); return ( -1 ); } // SVIファイルから、ファイル情報をget read_length = read(fd, svi_info, read_size); debug_log_output("read_length=%d, read_size=%d", read_length, read_size); if (read_length != read_size) { debug_log_output("fread() error."); return (-1); } // SVIファイルのデータ位置にSEEK lseek_ret = lseek(fd, SVI_REC_TIME_OFFSET, SEEK_SET); if ( lseek_ret < 0 ) { debug_log_output("lseek() error."); return ( -1 ); } // SVIファイルから、録画時間をGET read_length = read(fd, rec_time_work, SVI_REC_TIME_LENGTH ); debug_log_output("read_length=%d, SVI_REC_TIME_LENGTH=%d", read_length, SVI_FILENAME_LENGTH); if (read_length != SVI_REC_TIME_LENGTH) { debug_log_output("read() error."); return ( -1 ); } debug_log_output("read_rec=%02X,%02X", rec_time_work[0], rec_time_work[1] ); *rec_time += (unsigned int)( rec_time_work[0] ); *rec_time += (unsigned int)( rec_time_work[1] << 8 ); debug_log_output("rec_time=%d", *rec_time ); close( fd ); debug_log_output("read_filename = '%s'", svi_info); return ( 0 ); }
// ************************************************************************** // ファイルの実体の送信実行部 // ************************************************************************** off_t http_simple_file_send(int accept_socket, unsigned char *filename, off_t content_length, off_t range_start_pos ) { int fd; unsigned char *send_buf_p; ssize_t file_read_len; int data_send_len; off_t seek_ret; off_t total_read_size; size_t target_read_size; // ====================== // 送信バッファを確保 // ====================== send_buf_p = malloc(SEND_BUFFER_SIZE); if ( send_buf_p == NULL ) { debug_log_output("malloc() error.\n"); return ( 0 ); } // --------------------- // ファイルオープン // --------------------- fd = open(filename, O_RDONLY); if ( fd < 0 ) { debug_log_output("errno = %s\n", strerror(errno)); debug_log_output("open() error. file='%s'", filename); free(send_buf_p); return ( 0 ); } // ------------------------------------------ // range_start_posへファイルシーク // ------------------------------------------ seek_ret = lseek(fd, range_start_pos, SEEK_SET); if ( seek_ret < 0 ) // lseek エラーチェック { debug_log_output("errno = %s\n", strerror(errno)); debug_log_output("lseek() error."); free(send_buf_p); close(fd); return ( 0 ); } total_read_size = 0; // ================ // 実体転送開始 // ================ while ( (content_length==0) || (total_read_size < content_length) ) { // 一応バッファクリア memset(send_buf_p, 0, SEND_BUFFER_SIZE); // 目標readサイズ計算 if ( (content_length - total_read_size) > SEND_BUFFER_SIZE ) { target_read_size = SEND_BUFFER_SIZE; } else { target_read_size = (size_t)(content_length - total_read_size); } // ファイルからデータを読み込む。 file_read_len = read(fd, send_buf_p, target_read_size); if ( file_read_len <= 0 ) { debug_log_output("EOF detect.\n"); break; } // SOCKET にデータを送信 data_send_len = send(accept_socket, send_buf_p, file_read_len, 0); if ( data_send_len != file_read_len ) { debug_log_output("send() error.\n"); close(fd); // File Close. return ( total_read_size ); } total_read_size += file_read_len; } free(send_buf_p); // Memory Free. close(fd); // File Close. // 正常終了 return total_read_size; }