 /****************************************************************************

   mod_dav_pgsql (DASL) for apache 2.X
   CopyRight(C) 2005 SolutionBox Inc. All Rights reserved.
   CopyRight(C) 2006 Netomi Inc. All Rights reserved.
   Author : elenoa lazyfake (elenoa@solutionbox.co.kr)

   $Id: transfer.c,v 1.5 2007/02/22 08:22:13 elenoa Exp $

   Redistribution and use in source and binary forms, with or with out
   modification, are not permitted in outside of SolutionBox Inc.

 ****************************************************************************/

#include <httpd.h>
#include <http_config.h>
#include <http_protocol.h>
#include <http_log.h>
#include <http_core.h>      /* for ap_construct_url */
#include <http_request.h>

#include <apr_strings.h>
#include <apr_signal.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <pwd.h>
#include <unistd.h>
#include <math.h>
#include <string.h>
#include <stdint.h>

#include "dav_repos.h"
#include "dbms.h"
#include "util.h"
#include "share_statistic.h"
#include "transfer.h"

struct msg_mgnt {
	key_t key;
	int handle;
};

/* transfer queue log definition */

static int dav_initialize_transfer_log_queue(server_rec *s, apr_pool_t *p)
{
#ifdef __USE_TRANSFER_LOG__
#endif // __USE_TRANSFER_LOG__
	return OK;
}

static apr_status_t dav_cleanup_transfer_log_queue(void *data)
{
	/* do nothing. want something? */
	return 0;
}

int dav_transfer_log_generate(request_rec *r)
{
#ifdef __USE_TRANSFER_LOG__
#endif // __USE_TRANSFER_LOG__

	return OK;
}

/* transfer  progress queue   definition */
#define  MSG_PROGESS_KEY 642656000

struct Transfer_stat {
	long mtype; // 고정으로 1
	uint32_t timestamp;

	short int nInOut; // Inbount : 0  Outbout: non zero
	int nUserSeq;
	int nServiceSeq;
	uint64_t sizeTransfer;  // opendav에서는 순수 컨텐츠 전송량을 세팅해서 전송 하면 됨.
	// fimngd에서 rc_statd로 올려 줄때는 해당 변수에 1.1을 곱한 값도 별도로 전달 해줘야 함
	// mrtg와 유사한 형태를 맞추기 위해서 1.1을 곱함
	char filename_hash[128]; // 기존 message queue에서는 64byte 사용하고 있었지만, 추 후 변경 시 overflow를 방지하기 위해 일단 128byte로 결정 함
};

static struct msg_mgnt *progress_list = NULL;

static unsigned short cur_pos = 0;
static unsigned short max_progess = 0;

static int dav_initialize_transfer_progress_queue(server_rec *s, apr_pool_t *p)
{
	/* get dav_repos_server_conf structure */
	dav_repos_server_conf *dsc;
	dsc = dav_repos_get_server_conf(s);
	if (!dsc) {
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "OPENDAV: dav_initialize_transfer_progress_queue: "
			"fail to read config file");
		return !OK;
	}

	if (dsc->max_progress <= 0) {
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "OPENDAV: dav_initialize_transfer_progress_queue: "
			"MaxProgess <= 0 (%d)", dsc->max_progress);
		return !OK;
	}
	
	max_progess = dsc->max_progress;
	progress_list = malloc(sizeof(struct msg_mgnt) * max_progess);
	if (progress_list == NULL)
	{ 
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "OPENDAV: dav_initialize_transfer_progress_queue: "
			"Memory allocation failure");
		return !OK;
	}
	
	int i = 0;
	for (i = 0; i < max_progess; i++)
	{
		/* create or attach queue */
		progress_list[i].key = MSG_PROGESS_KEY + i;
		if ((progress_list[i].handle = msgget(progress_list[i].key, 0644 | IPC_EXCL)) == -1)
		{
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "OPENDAV: dav_initialize_transfer_progress_queue: cannot attach [%d][%s]", i, strerror(errno));
			return !OK;
		}
	}

	return OK;
}

static apr_status_t dav_cleanup_transfer_progress_queue(void *data)
{
	if (progress_list)
		free(progress_list);

	return 0;
}

static int dav_transfer_progess_set_value(struct Transfer_stat *ts)
{
	if (progress_list == NULL)
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_transfer_progess_set_value: progress list is NULL. [%s]", ts->filename_hash);
		return !OK;
	}

	if ((++cur_pos) >= max_progess)
		cur_pos = 0;

	int err = errno;
	int i = 0;
	
	for (i = cur_pos; i < max_progess; i++)
	{
		if (msgsnd(progress_list[i].handle, (const void *)ts, sizeof(struct Transfer_stat), IPC_NOWAIT) != -1)
			return OK;
		err = errno;

		if (err == EINTR)
		{
			apr_sleep(10000);
		}
	}

	for (i = 0; i < max_progess; i++)
	{
		if (msgsnd(progress_list[i].handle, (const void *)ts, sizeof(struct Transfer_stat), IPC_NOWAIT) != -1)
			return OK;
		err = errno;

		if (err == EINTR)
		{
			apr_sleep(10000);
		}
	}

	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_transfer_progess_set_value: fail. [%s] [%s(%d)]", ts->filename_hash, strerror(err), err);
	return !OK;	
}

int dav_transfer_progess_generate(request_rec *r, int bound, apr_size_t size, apr_size_t org_sent)
{
	char *volume = r->user;

	if (r == NULL) return OK;
	
	if (size < 0) return OK;
	
	if (!volume) return OK;

	acct_info acctinfo;
	memset(&acctinfo, 0x00, sizeof(acct_info));

	if (at_split(r->user, &acctinfo) <= 0)
		return OK;
	
	struct Transfer_stat ts = { 0 };
	
	ts.nServiceSeq = atoi(acctinfo.szVolumeID);
	ts.nUserSeq = atoi(acctinfo.szUserID);

	ts.timestamp = STAT_STAMP(time(0));

	if (ts.nServiceSeq == 0 && ts.nUserSeq == 0)
		return OK;

	ts.mtype = 1;
	ts.nInOut = bound;
	ts.sizeTransfer = size;

	strcpy(ts.filename_hash, r->canonical_filename);

	if (dav_transfer_progess_set_value(&ts) == !OK)
	{
		//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dav_transfer_progess_set_value: fail. [%s]", r->uri);
	}

	return OK;
}


/************************/
/* initialize functions */

int dav_initialize_transfer_queue(server_rec *s, apr_pool_t *p)
{
	dav_initialize_transfer_log_queue(s, p);
	if (dav_initialize_transfer_progress_queue(s, p) != OK)
		return !OK;

	return OK;
}

apr_status_t dav_cleanup_transfer_queue(void *data)
{
	/* do nothing. want something? */
	dav_cleanup_transfer_log_queue(data);
	dav_cleanup_transfer_progress_queue(data);

	return 0;
}



