/*
* File : wn_module_dav.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
*
* This software is dual-licensed: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. For the terms of this
* license, see .
*
* You are free to use this software under the terms of the GNU General
* Public License, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* Alternatively for commercial application, you can contact us
* by email for commercial license.
*
* Change Logs:
* Date Author Notes
* 2015-9-9 Coing the first version
*/
#include
#include
#include
#include
#ifdef RT_USING_DFS
#include
#endif
#define DBG_ENABLE
#define DBG_COLOR
#define DBG_SECTION_NAME "wn.dav"
#ifdef WEBNET_USING_LOG
#define DBG_LEVEL DBG_LOG
#else
#define DBG_LEVEL DBG_INFO
#endif /* WEBNET_USING_LOG */
#include
#ifdef WEBNET_USING_DAV
struct webnet_module_put_entry
{
int (*put_open) (struct webnet_session* session);
int (*put_close)(struct webnet_session* session);
int (*put_write)(struct webnet_session* session, const void* data, rt_size_t length);
int (*put_done) (struct webnet_session* session);
};
struct webnet_module_put_session
{
rt_uint16_t file_opened;
/* put entry */
const struct webnet_module_put_entry* entry;
rt_uint32_t user_data;
};
static const char* propfind_element =""
"%s"
""
""
"%s"
"%d"
"%s" // Sat, 05 Sep 2015 09:47:53 GMT
""
"HTTP/1.1 200 OK"
""
"\n";
static const void* webnet_put_get_userdata(struct webnet_session* session);
int webnet_module_put_method(struct webnet_session* session);
static void print_propfind_element(struct webnet_session *session, const char *uri,struct stat *stp);
int webnet_module_dav(struct webnet_session* session, int event)
{
if (event != WEBNET_EVENT_URI_POST)
return WEBNET_MODULE_CONTINUE;
if(session->request->method == WEBNET_OPTIONS)
{
static const char* status = "Allow: GET, POST, HEAD, PUT, DELETE, OPTIONS, PROPFIND, MKCOL\r\nDAV: 1\r\n\r\n";
LOG_D("OPTIONS %s", session->request->path);
session->request->result_code = 200;
webnet_session_set_header_status_line(session, session->request->result_code, "OK");
webnet_session_printf(session, status);
return WEBNET_MODULE_FINISHED;
}
else if(session->request->method == WEBNET_PROPFIND)
{
int fd;
DIR *dir;
rt_uint8_t exit_file = 0;
struct stat file_stat;
const char* parent_path;
static const char header[] = "HTTP/1.1 207 Multi-Status\r\n"
"Connection: close\r\n"
"Content-Type: text/xml; charset=utf-8\r\n\r\n"
""
"\n";
static const char footer[] = "";
char *depth = session->request->depth;
LOG_D("PROPFIND %s depth: %s", session->request->path,
session->request->depth?session->request->depth:"null");
fd = open(session->request->path, O_RDONLY, 0);
if (fd >= 0)
{
close(fd);
exit_file = 1;
}
dir = opendir(session->request->path);
if(dir != RT_NULL)
{
closedir(dir);
exit_file = 1;
}
if(!exit_file)
{
LOG_E("Open file(%s) is not exist.", session->request->path);
session->request->result_code = 404;
return WEBNET_MODULE_FINISHED;
}
webnet_session_printf(session, header);
parent_path = session->request->path + strlen(webnet_get_root());
/* output the parent. */
file_stat.st_size = 0;
file_stat.st_mtime = 0;
file_stat.st_mode = S_IFDIR;
print_propfind_element(session, parent_path, &file_stat);
/* depth: 0, 1, infinity. */
if( (depth == NULL || (strcmp(depth, "0") != 0) ) )
{
struct dirent* dirent;
char *fullpath;
fullpath = wn_malloc (WEBNET_PATH_MAX);
rt_memset(&file_stat, 0, sizeof(struct stat));
//eg. dir = opendir("/webnet/SD");
dir = opendir(session->request->path);
while((dirent = readdir(dir)) != NULL)
{
/* build full path for each file */
rt_sprintf(fullpath, "%s/%s",session->request->path, dirent->d_name);
str_normalize_path(fullpath);
stat(fullpath, &file_stat);
print_propfind_element(session, dirent->d_name,&file_stat);
}
closedir(dir);
wn_free(fullpath);
}
webnet_session_printf(session,footer);
return WEBNET_MODULE_FINISHED;
}
else if(session->request->method == WEBNET_PUT)
{
LOG_D("PUT %s", session->request->path);
return webnet_module_put_method(session);
}
else if(session->request->method == WEBNET_PROPPATCH)
{
int proppatch_length = 0;
static const char Proppatch_header[] = "HTTP/1.1 207 Multi-Status\r\n"
//"Date: Mon, 07 Sep 2015 01:50:42 GMT\r\n"
"Server: %s %s\r\n"
"Content-Length: %d \r\n"
"Content-Type: text/xml; charset=\"utf-8\"\r\n\r\n";
static const char Proppatch_ex[] = ""
""
""
"%s"
""
""
""
"HTTP/1.1 409 (status)"
""
"Property is read-only."
""
""
"";
LOG_D("PROPPATCH %s", session->request->path);
proppatch_length = strlen(Proppatch_ex)+strlen(session->request->path)-2;
webnet_session_printf(session,Proppatch_header,WEBNET_THREAD_NAME,WEBNET_VERSION,proppatch_length);
webnet_session_printf(session,Proppatch_ex,session->request->path);
return WEBNET_MODULE_FINISHED;
}
else if (session->request->method == WEBNET_DELETE)
{
LOG_D("DELETE %s", session->request->path);
unlink(session->request->path);
webnet_session_printf(session,"HTTP/1.1 204 No Content\r\n"
"Server: webnet\r\n"
"Content-Length: 0\r\n"
"Content-Type: text/plain\r\n\r\n");
return WEBNET_MODULE_FINISHED;
}
else if (session->request->method == WEBNET_MKCOL)
{
int ret;
LOG_D("MKCOL %s", session->request->path);
ret = mkdir(session->request->path, 0x777);
if (ret < 0)
{
session->request->result_code = 404;
LOG_E("MKCOL mkdir error, path %s.", session->request->path);
}
else
{
LOG_D("MKCOL mkdir ok !");
}
webnet_session_printf(session,"HTTP/1.1 201 Created\r\n"
"Server: webnet\r\n"
"Content-Length: 0\r\n"
"Content-Type: text/plain\r\n\r\n");
return WEBNET_MODULE_FINISHED;
}
else if (session->request->method == WEBNET_MOVE)
{
char *path, *full_path;
path = strstr(session->request->destination, session->request->host);
if (path)
{
path += strlen(session->request->host);
full_path = (char*) wn_malloc (WEBNET_PATH_MAX);
if (full_path)
{
webnet_session_get_physical_path(session, path, full_path);
LOG_D("Get full path, %s => %s\n", session->request->path, full_path);
rename(session->request->path, full_path);
wn_free(full_path);
}
}
webnet_session_printf(session,"HTTP/1.1 200 OK\r\n"
"Server: webnet\r\n"
"Content-Length: 0\r\n"
"Content-Type: text/plain\r\n\r\n");
return WEBNET_MODULE_FINISHED;
}
return WEBNET_MODULE_CONTINUE;
}
#include
static void print_propfind_element(struct webnet_session *session, const char *uri, struct stat *file_stat)
{
char ctime_str[64];
time_t t = file_stat->st_mtime;
rt_enter_critical();
strftime(ctime_str, sizeof(ctime_str), "%a, %d %b %Y %H:%M:%S GMT", localtime(&t));
rt_exit_critical();
//LOG_D("strftime: %s\n", ctime_str);
webnet_session_printf(session,
propfind_element,
uri, S_ISDIR(file_stat->st_mode) ? "" : "",
file_stat->st_size, ctime_str);
}
static int put_open (struct webnet_session* session)
{
int fd;
if(session->request->path == RT_NULL)
{
fd = -1;
goto _exit;
}
fd = open(session->request->path, O_WRONLY | O_CREAT | O_TRUNC, 0);
if(fd < 0)
{
session->session_phase = WEB_PHASE_CLOSE;
fd = -1;
goto _exit;
}
return fd;
_exit:
LOG_E(" %s failed ,file:%s ,line:%d",__FUNCTION__,__FILE__,__LINE__);
return (int)fd;
}
static int put_close(struct webnet_session* session)
{
int fd;
fd = (int)webnet_put_get_userdata(session);
if (fd < 0) return 0;
close(fd);
return 0;
}
static int put_done (struct webnet_session* session)
{
int put_done_h_length = 0;
static const char put_done_h[] = "HTTP/1.1 201 Created\r\n"
"Date: Mon, 07 Sep 2015 01:50:42 GMT\r\n"
"Server: %s %s\r\n"
"Location: http://%s%s \r\n"
"Content-Length: %d \r\n"
"Content-Type: text/html; charset=ISO-8859-1\r\n\r\n";
static const char put_done_t[] =""
""
"";
LOG_D("put_done.");
put_done_h_length = strlen(put_done_t);
webnet_session_printf(session,put_done_h,WEBNET_THREAD_NAME,WEBNET_VERSION,inet_ntoa(session->cliaddr.sin_addr),session->request->path,put_done_h_length);
webnet_session_printf(session,put_done_t);
return 0;
}
static int put_write(struct webnet_session* session, const void* data, rt_size_t length)
{
int fd;
// get fd
fd = (int)webnet_put_get_userdata(session);
if (fd < 0) return 0;
write(fd, data, length);
return length;
}
static const void* webnet_put_get_userdata(struct webnet_session* session)
{
struct webnet_module_put_session *put_session;
/* get put session */
put_session = (struct webnet_module_put_session *)session->user_data;
if (put_session == RT_NULL) return RT_NULL;
return (const void*) put_session->user_data;
}
static void _webnet_module_put_close(struct webnet_session* session)
{
struct webnet_module_put_session *put_session;
/* get put session */
put_session = (struct webnet_module_put_session *)session->user_data;
if (put_session == RT_NULL) return;
/* close file */
if (put_session->file_opened == 1)
{
put_session->entry->put_close(session);
put_session->file_opened = 0;
}
wn_free(put_session);
/* remove private data */
session->user_data = 0;
session->session_ops = RT_NULL;
}
static void _webnet_module_put_handle(struct webnet_session* session, int event)
{
if(session->request->method == WEBNET_PUT)
{
int length = 0;
static int read_bytes = 0;
struct webnet_module_put_session *put_session;
put_session = (struct webnet_module_put_session *)session->user_data;
/* read stream */
length = webnet_session_read(session, (char *)session->buffer, sizeof(session->buffer) - 1);
/* connection break out */
if (length <= 0)
{
LOG_E(" %s failed ,file:%s ,line:%d\n",__FUNCTION__,__FILE__,__LINE__);
/* read stream failed (connection break out), close this session */
session->session_phase = WEB_PHASE_CLOSE;
return;
}
read_bytes = read_bytes + length;
session->buffer[length] = '\0';
/* open file */
if (put_session->file_opened == 0)
{
/* open file */
put_session->user_data = put_session->entry->put_open(session);
put_session->file_opened = 1;
}
//write data
if (length > 0 && session->buffer != NULL)
{
put_session->entry->put_write(session, (char*)session->buffer, length);
}
if (read_bytes >= (session->request->content_length))
{
put_session->entry->put_done(session);
_webnet_module_put_close(session);
read_bytes = 0;
}
}
}
static const struct webnet_session_ops _put_ops =
{
_webnet_module_put_handle,
_webnet_module_put_close
};
const struct webnet_module_put_entry put_entry_put =
{
put_open,
put_close,
put_write,
put_done
};
int webnet_module_put_method(struct webnet_session* session)
{
const struct webnet_module_put_entry *entry = &put_entry_put;
struct webnet_module_put_session *put_session;
/* create a uploading session */
put_session = (struct webnet_module_put_session*) wn_malloc (sizeof (struct webnet_module_put_session));
if(put_session == RT_NULL)
{
LOG_E("No memory for module put session.");
return WEBNET_MODULE_CONTINUE;
}
put_session->file_opened = 0;
put_session->entry = entry;
put_session->user_data = 0;
/* add this put session into webnet session */
session->user_data = (rt_uint32_t) put_session;
/* set webnet session operations */
session->session_ops = &_put_ops;
return WEBNET_MODULE_FINISHED;
}
#endif /* WEBNET_USING_DAV */