WebNet_XiUOS/module/wn_module_dav.c

487 lines
15 KiB
C

/*
* 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 <http://www.gnu.org/licenses/>.
*
* 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 <business@rt-thread.com> for commercial license.
*
* Change Logs:
* Date Author Notes
* 2022-05-09 chunyexixiaoyu the version transplanted from rt-thread
*/
#include <transform.h>
#include <webnet.h>
#include <wn_session.h>
#include <wn_module.h>
#include <wn_utils.h>
#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, uint32 length);
int (*put_done) (struct webnet_session* session);
};
struct webnet_module_put_session
{
uint16 file_opened;
/* put entry */
const struct webnet_module_put_entry* entry;
uint32 user_data;
};
static const char* propfind_element ="<d:response>"
"<d:href>%s</d:href>"
"<d:propstat>"
"<d:prop>"
"<d:resourcetype>%s</d:resourcetype>"
"<d:getcontentlength>%d</d:getcontentlength>"
"<d:getlastmodified>%s</d:getlastmodified>" // Sat, 05 Sep 2015 09:47:53 GMT
"</d:prop>"
"<d:status>HTTP/1.1 200 OK</d:status>"
"</d:propstat>"
"</d:response>\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";
printf("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;
uint8 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"
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<d:multistatus xmlns:d='DAV:'>\n";
static const char footer[] = "</d:multistatus>";
char *depth = session->request->depth;
printf("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 != NULL)
{
closedir(dir);
exit_file = 1;
}
if(!exit_file)
{
printf("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);
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 */
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)
{
printf("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[] = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<D:multistatus xmlns:D=\"DAV:\" xmlns:ns0=\"DAV:\">"
"<D:response>"
"<D:href>%s</D:href>"
"<D:propstat>"
"<D:prop><ns0:getlastmodified/>"
"</D:prop>"
"<D:status>HTTP/1.1 409 (status)</D:status>"
"<D:responsedescription>"
"Property is read-only.</D:responsedescription>"
"</D:propstat>"
"</D:response>"
"</D:multistatus>";
printf("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)
{
printf("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;
printf("MKCOL %s", session->request->path);
ret = mkdir(session->request->path, 0x777);
if (ret < 0)
{
session->request->result_code = 404;
printf("MKCOL mkdir error, path %s.", session->request->path);
}
else
{
printf("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);
printf("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 <time.h>
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;
PrivTaskenterCritical();
strftime(ctime_str, sizeof(ctime_str), "%a, %d %b %Y %H:%M:%S GMT", localtime(&t));
PrivTaskexitCritical();
//printf("strftime: %s\n", ctime_str);
webnet_session_printf(session,
propfind_element,
uri, S_ISDIR(file_stat->st_mode) ? "<d:collection/>" : "",
file_stat->st_size, ctime_str);
}
static int put_open (struct webnet_session* session)
{
int fd;
if(session->request->path == 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:
printf(" %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[] ="<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">"
"<html><head>"
"<title>";
printf("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, uint32 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 == NULL) return 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 == 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 = 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)
{
printf(" %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 == NULL)
{
printf("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 = (uint32) put_session;
/* set webnet session operations */
session->session_ops = &_put_ops;
return WEBNET_MODULE_FINISHED;
}
#endif /* WEBNET_USING_DAV */