/**
 * FTPM(FTPもどき)
 * Server側プログラム
 *
 * 常時ファイル受信待ち
 * 受信ファイルサイズはserver.confで変更可能
 * 受信ファイルはdownloadフォルダに保存
 *
 *
 **/

#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <nstdc_socket.h>

#define READ_SIZE 256
#define FILE_NAME_MAX 100

void printSockInf( struct sockaddr_in* inf );
int getByteData( int data, int dataPos );

bool recvCommand( int sock );
void recvData( int sock );
void getFileName( int sock );
void getData( int sock );

FILE *fp;
int main( void ) {
	int socket0;				/* bindするsocketの識別子 */
	struct sockaddr_in addr;	/* サーバー自身の情報 */
	struct sockaddr_in client;	/* クライアントの情報 */
	socklen_t len;				/* クライアント情報のサイズ */
	int sock;					/* 接続するsocketの識別子 */
	int ret;					/* 戻り値 */
	int yes = 1;

	char inbuf[READ_SIZE];	/* 受信データ領域 */
	
	/* 領域初期化 */
	memset( inbuf, 0, sizeof( inbuf ));

	/* ソケットの生成 */
	socket0 = socket( AF_INET, SOCK_STREAM, 0 );

	if( socket0 < 0 ){
		perror("socket");
		return 1;
	}

	/* ソケットの設定 */
	addr.sin_family = AF_INET;			/* IPv4 */
	addr.sin_port = htons( 21080 );
	addr.sin_addr.s_addr = INADDR_ANY;
	
	setsockopt( socket0, SOL_SOCKET, SO_REUSEADDR, (const char*)&yes, sizeof(yes));
	
	ret = bind( socket0, ( struct sockaddr * )&addr, sizeof( addr ) );
	if( ret != 0 ){
		perror("bind");
		return 1;
	}	

	/* TCPクライアントからの接続要求を待てる状態にする */
	ret = listen( socket0, 5 );
	if( ret != 0 ){
		perror("listen");
		return 1;
	}

	/* 繰り返し接続可能にする */
	while(1) 
	{
		/* TCPクライアントからの接続要求を受け付ける */
		len = sizeof( client );
		sock = accept( socket0, ( struct sockaddr *)&client, &len );
		if( sock < 0 ){
			perror( "accept" );
			return 1;
		}

	   	/* 接続情報の表示 */
		printf("accepted connection from IP:");
		printSockInf(&client);

		/* データを受信する */
		recvData( sock );

		/* TCPセッションの終了 */
		while( recv( sock, inbuf, sizeof( inbuf ) - 1, 0 ) > 0 ) { }
		close( sock );

	}

	/* listenするsocketの終了 */
	close( socket0 );

	return 0;

}

/* データを受信する */
void recvData( const int sock ){

	bool isEnd = false;		

	/* データ受信が完了するまでコマンド別の処理を実行する */
	while( !isEnd ){
		isEnd = recvCommand( sock );
	}
}

/* コマンドを受信・実行する */
bool recvCommand( const int sock ){
	char command;
	char commandEnd;

	bool isLastCommand = false;

	/* コマンドを受信する */
	recv( sock, &command, 1, 0 );
	recv( sock, &commandEnd, 1, 0 );
	
	printf(" RECV = %s\n", &command );
	if( commandEnd != ':' ){
		printf("ERROR NO COMMAND.\n");
		return true;
	}

	/* コマンドを解析し、コマンド別の処理を実行する */
	switch( command ){
		case 'n':		/* ファイル名指定コマンド */
			printf("command FileName\n");
		
			/* ファイル名を読み取って書き込みファイルを作成する. */
			getFileName( sock );
			break;
		case 't':		/* ファイル送信コマンド */
			printf("command File\n");
		
			/* readが空になるまで読み取りファイルに書き込む。 */
			getData( sock );

			isLastCommand = true;
			break;
		default :
			break;
	}

	return isLastCommand;
}

/* ファイル名を読み取り、同名のファイルを生成・オープンする */
void getFileName( int sock ){
	char read;
	char fname[FILE_NAME_MAX];
	int i = 0;
	printf("getFileName\n");
	
	/* 一文字ずつ改行コードまで読み取る */
	recv( sock, &read, 1, 0);

	while( read != '\n' ){
//		printf("%c\n",read);
		fname[i] = read;
		recv( sock, &read, 1, 0 );
		i++;
	}
	fname[i] = '\0';
	printf("fileName = %s \n", &fname );

	/* 読み取ったファイル名でファイルをオープンする */
	fp = fopen( fname, "w");
	if( fp == NULL ){
		printf("ERROR File not open.\n");
		return;
	}
}

/* データをすべて受信しオープン済みのファイルに書き込む */
void getData( int sock ){
	char buf[READ_SIZE];

	/* データをすべて読み取りファイルに書き込む */
	size_t size = recv(sock, buf, READ_SIZE, 0);

	while( size > 0 ){
		printf("size = %d\n%s\n", size, buf);
		fwrite( buf, sizeof( char ), size, fp );
		size = recv(sock, buf, READ_SIZE, 0);
	}

	/* ファイルをクローズする */
	fclose( fp );

	return;
}

/* socket情報からアドレス情報を表示する */
void printSockInf( struct sockaddr_in* inf ){
	in_addr_t addr;		/* IPアドレス */
	addr = ntohl( inf->sin_addr.s_addr );
	printf( "%d.%d.%d.%d(%d)\n", 
				getByteData( addr, 3 ),
				getByteData( addr, 2 ),
				getByteData( addr, 1 ),
				getByteData( addr, 0 ),
				inf->sin_port);

	return;
}		

/* 指定のデータ(int32)から指定位置の1Byteを返す
 * dataPos : data内の位置指定(右端から0オリジン) [3][2][1][0] */
int getByteData( int data, int dataPos ){
	return (( data >> ( dataPos * 8 )) & 0xFF );
} 

/* ----- EOF ----- */

