/**
* Copyright (C) 2008 Happy Fish / YuQing
*
* FastDFS may be copied only under the terms of the GNU General
* Public License V3, which may be found in the FastDFS source kit.
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
**/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <time.h>
#include <unistd.h>
#include "fastcommon/logger.h"
#include "fastcommon/shared_func.h"
#include "fastcommon/sockopt.h"
#include "fastcommon/http_func.h"
#include "fastcommon/local_ip_func.h"
#include "fastdfs/fdfs_define.h"
#include "fastdfs/fdfs_global.h"
#include "fastdfs/fdfs_http_shared.h"
#include "fastdfs/fdfs_client.h"
#include "fastdfs/fdfs_shared_func.h"
#include "fastdfs/trunk_shared.h"
#include "common.h"
#define FDFS_MOD_REPONSE_MODE_PROXY 'P'
#define FDFS_MOD_REPONSE_MODE_REDIRECT 'R'
#define FDFS_CONTENT_TYPE_TAG_STR "Content-type: "
#define FDFS_CONTENT_TYPE_TAG_LEN (sizeof(FDFS_CONTENT_TYPE_TAG_STR) - 1)
#define FDFS_CONTENT_RANGE_TAG_STR "Content-range: "
#define FDFS_CONTENT_RANGE_TAG_LEN (sizeof(FDFS_CONTENT_RANGE_TAG_STR) - 1)
static char flv_header[] = "FLV\x1\x1\0\0\0\x9\0\0\0\x9";
#define FDFS_RANGE_LENGTH(range) ((range.end - range.start) + 1)
typedef struct tagGroupStorePaths {
char group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
int group_name_len;
int storage_server_port;
FDFSStorePaths store_paths;
} GroupStorePaths;
static int storage_server_port = FDFS_STORAGE_SERVER_DEF_PORT;
static int my_group_name_len = 0;
static int group_count = 0; //for multi groups
static bool url_have_group_name = false;
static bool use_storage_id = false;
static bool flv_support = false; //if support flv
static char flv_extension[FDFS_FILE_EXT_NAME_MAX_LEN + 1] = {0}; //flv extension name
static int flv_ext_len = 0; //flv extension length
static char my_group_name[FDFS_GROUP_NAME_MAX_LEN + 1] = {0};
static char response_mode = FDFS_MOD_REPONSE_MODE_PROXY;
static GroupStorePaths *group_store_paths = NULL; //for multi groups
static FDFSHTTPParams g_http_params;
static int storage_sync_file_max_delay = 24 * 3600;
static int fdfs_get_params_from_tracker();
static int fdfs_format_http_datetime(time_t t, char *buff, const int buff_size);
static int fdfs_strtoll(const char *s, int64_t *value)
{
char *end = NULL;
*value = strtoll(s, &end, 10);
if (end != NULL && *end != '\0')
{
return EINVAL;
}
return 0;
}
static int fdfs_load_groups_store_paths(IniContext *pItemContext)
{
char section_name[64];
char *pGroupName;
int bytes;
int result;
int i;
bytes = sizeof(GroupStorePaths) * group_count;
group_store_paths = (GroupStorePaths *)malloc(bytes);
if (group_store_paths == NULL)
{
logError("file: "__FILE__", line: %d, " \
"malloc %d bytes fail, " \
"errno: %d, error info: %s", \
__LINE__, bytes, errno, STRERROR(errno));
return errno != 0 ? errno : ENOMEM;
}
for (i=0; i<group_count; i++)
{
sprintf(section_name, "group%d", i + 1);
pGroupName = iniGetStrValue(section_name, "group_name", \
pItemContext);
if (pGroupName == NULL)
{
logError("file: "__FILE__", line: %d, " \
"section: %s, you must set parameter: " \
"group_name!", __LINE__, section_name);
return ENOENT;
}
group_store_paths[i].storage_server_port = iniGetIntValue( \
section_name, "storage_server_port", pItemContext, \
FDFS_STORAGE_SERVER_DEF_PORT);
group_store_paths[i].group_name_len = snprintf( \
group_store_paths[i].group_name, \
sizeof(group_store_paths[i].group_name), \
"%s", pGroupName);
if (group_store_paths[i].group_name_len == 0)
{
logError("file: "__FILE__", line: %d, " \
"section: %s, parameter: group_name " \
"can't be empty!", __LINE__, section_name);
return EINVAL;
}
group_store_paths[i].store_paths.paths = \
storage_load_paths_from_conf_file_ex(pItemContext, \
section_name, false, &group_store_paths[i].store_paths.count, \
&result);
if (result != 0)
{
return result;
}
}
return 0;
}
int fdfs_mod_init()
{
IniContext iniContext;
int result;
int len;
int i;
char *pLogFilename;
char *pReponseMode;
char *pIfAliasPrefix;
char buff[2 * 1024];
bool load_fdfs_parameters_from_tracker = false;
log_init();
trunk_shared_init();
if ((result=iniLoadFromFile(FDFS_MOD_CONF_FILENAME, &iniContext)) != 0)
{
logError("file: "__FILE__", line: %d, " \
"load conf file \"%s\" fail, ret code: %d", \
__LINE__, FDFS_MOD_CONF_FILENAME, result);
return result;
}
do
{
group_count = iniGetIntValue(NULL, "group_count", &iniContext, 0);
if (group_count < 0)
{
logError("file: "__FILE__", line: %d, " \
"conf file: %s, group_count: %d is invalid!", \
__LINE__, FDFS_MOD_CONF_FILENAME, group_count);
return EINVAL;
}
url_have_group_name = iniGetBoolValue(NULL, "url_have_group_name", \
&iniContext, false);
if (group_count > 0)
{
if (!url_have_group_name)
{
logError("file: "__FILE__", line: %d, " \
"config file: %s, you must set " \
"url_have_group_name to true to " \
"support multi-group!", \
__LINE__, FDFS_MOD_CONF_FILENAME);
result = ENOENT;
break;
}
if ((result=fdfs_load_groups_store_paths(&iniContext)) != 0)
{
break;
}
}
else
{
char *pGroupName;
pGroupName = iniGetStrValue(NULL, "group_name", &iniContext);
if (pGroupName == NULL)
{
logError("file: "__FILE__", line: %d, " \
"config file: %s, you must set parameter: " \
"group_name!", __LINE__, FDFS_MOD_CONF_FILENAME);
result = ENOENT;
break;
}
my_group_name_len = snprintf(my_group_name, \
sizeof(my_group_name), "%s", pGroupName);
if (my_group_name_len == 0)
{
logError("file: "__FILE__", line: %d, " \
"config file: %s, parameter: group_name " \
"can't be empty!", __LINE__, \
FDFS_MOD_CONF_FILENAME);
result = EINVAL;
break;
}
if ((result=storage_load_paths_from_conf_file(&iniContext)) != 0)
{
break;
}
}
g_fdfs_connect_timeout = iniGetIntValue(NULL, "connect_timeout", \
&iniContext, DEFAULT_CONNECT_TIMEOUT);
if (g_fdfs_connect_timeout <= 0)
{
g_fdfs_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
}
g_fdfs_network_timeout = iniGetIntValue(NULL, "network_timeout", \
&iniContext, DEFAULT_NETWORK_TIMEOUT);
if (g_fdfs_network_timeout <= 0)
{
g_fdfs_network_timeout = DEFAULT_NETWORK_TIMEOUT;
}
load_log_level(&iniContext);
pLogFilename = iniGetStrValue(NULL, "log_filename", &iniContext);
if (pLogFilename != NULL && *pLogFilename != '\0')
{
if ((result=log_set_filename(pLogFilename)) != 0)
{
break;
}
}
storage_server_port = iniGetIntValue(NULL, "storage_server_port", \
&iniContext, FDFS_STORAGE_SERVER_DEF_PORT);
if ((result=fdfs_http_params_load(&iniContext, FDFS_MOD_CONF_FILENAME, \
&g_http_params)) != 0)
{
break;
}
pReponseMode = iniGetStrValue(NULL, "response_mode", &iniContext);
if (pReponseMode != NULL)
{
if (strcmp(pReponseMode, "redirect") == 0)
{
response_mode = FDFS_MOD_REPONSE_MODE_REDIRECT;
}
}
pIfAliasPrefix = iniGetStrValue (NULL, "if_alias_prefix", &iniContext);
if (pIfAliasPrefix == NULL)
{
*g_if_alias_prefix = '\0';
}
else
{
snprintf(g_if_alias_prefix, sizeof(g_if_alias_prefix),
"%s", pIfAliasPrefix);
}
load_fdfs_parameters_from_tracker = iniGetBoolValue(NULL, \
"load_fdfs_parameters_from_tracker", \
&iniContext, false);
if (load_fdfs_parameters_from_tracker)
{
result = fdfs_load_tracker_group_ex(&g_tracker_group, \
FDFS_MOD_CONF_FILENAME, &iniContext);
}
else
{
storage_sync_file_max_delay = iniGetIntValue(NULL, \
"storage_sync_file_max_delay", \
&iniContext, 24 * 3600);
use_storage_id = iniGetBoolValue(NULL, "use_storage_id", \
&iniContext, false);
if (use_storage_id)
{
result = fdfs_load_storage_ids_from_file( \
FDFS_MOD_CONF_FILENAME, &i