wn_module_dav.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. /*
  2. * File : wn_module_dav.c
  3. * This file is part of RT-Thread RTOS
  4. * COPYRIGHT (C) 2006 - 2018, RT-Thread Development Team
  5. *
  6. * This software is dual-licensed: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License version 2 as
  8. * published by the Free Software Foundation. For the terms of this
  9. * license, see <http://www.gnu.org/licenses/>.
  10. *
  11. * You are free to use this software under the terms of the GNU General
  12. * Public License, but WITHOUT ANY WARRANTY; without even the implied
  13. * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  14. * See the GNU General Public License for more details.
  15. *
  16. * Alternatively for commercial application, you can contact us
  17. * by email <business@rt-thread.com> for commercial license.
  18. *
  19. * Change Logs:
  20. * Date Author Notes
  21. * 2015-9-9 Coing the first version
  22. */
  23. #include <webnet.h>
  24. #include <wn_session.h>
  25. #include <wn_module.h>
  26. #include <wn_utils.h>
  27. #ifdef RT_USING_DFS
  28. #include <dfs_posix.h>
  29. #endif
  30. #define DBG_ENABLE
  31. #define DBG_COLOR
  32. #define DBG_SECTION_NAME "wn.dav"
  33. #ifdef WEBNET_USING_LOG
  34. #define DBG_LEVEL DBG_LOG
  35. #else
  36. #define DBG_LEVEL DBG_INFO
  37. #endif /* WEBNET_USING_LOG */
  38. #include <rtdbg.h>
  39. #ifdef WEBNET_USING_DAV
  40. struct webnet_module_put_entry
  41. {
  42. int (*put_open) (struct webnet_session* session);
  43. int (*put_close)(struct webnet_session* session);
  44. int (*put_write)(struct webnet_session* session, const void* data, rt_size_t length);
  45. int (*put_done) (struct webnet_session* session);
  46. };
  47. struct webnet_module_put_session
  48. {
  49. rt_uint16_t file_opened;
  50. /* put entry */
  51. const struct webnet_module_put_entry* entry;
  52. rt_uint32_t user_data;
  53. };
  54. static const char* propfind_element ="<d:response>"
  55. "<d:href>%s</d:href>"
  56. "<d:propstat>"
  57. "<d:prop>"
  58. "<d:resourcetype>%s</d:resourcetype>"
  59. "<d:getcontentlength>%d</d:getcontentlength>"
  60. "<d:getlastmodified>%s</d:getlastmodified>" // Sat, 05 Sep 2015 09:47:53 GMT
  61. "</d:prop>"
  62. "<d:status>HTTP/1.1 200 OK</d:status>"
  63. "</d:propstat>"
  64. "</d:response>\n";
  65. static const void* webnet_put_get_userdata(struct webnet_session* session);
  66. int webnet_module_put_method(struct webnet_session* session);
  67. static void print_propfind_element(struct webnet_session *session, const char *uri,struct stat *stp);
  68. int webnet_module_dav(struct webnet_session* session, int event)
  69. {
  70. if (event != WEBNET_EVENT_URI_POST)
  71. return WEBNET_MODULE_CONTINUE;
  72. if(session->request->method == WEBNET_OPTIONS)
  73. {
  74. static const char* status = "Allow: GET, POST, HEAD, PUT, DELETE, OPTIONS, PROPFIND, MKCOL\r\nDAV: 1\r\n\r\n";
  75. LOG_D("OPTIONS %s", session->request->path);
  76. session->request->result_code = 200;
  77. webnet_session_set_header_status_line(session, session->request->result_code, "OK");
  78. webnet_session_printf(session, status);
  79. return WEBNET_MODULE_FINISHED;
  80. }
  81. else if(session->request->method == WEBNET_PROPFIND)
  82. {
  83. int fd;
  84. DIR *dir;
  85. rt_uint8_t exit_file = 0;
  86. struct stat file_stat;
  87. const char* parent_path;
  88. static const char header[] = "HTTP/1.1 207 Multi-Status\r\n"
  89. "Connection: close\r\n"
  90. "Content-Type: text/xml; charset=utf-8\r\n\r\n"
  91. "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
  92. "<d:multistatus xmlns:d='DAV:'>\n";
  93. static const char footer[] = "</d:multistatus>";
  94. char *depth = session->request->depth;
  95. LOG_D("PROPFIND %s depth: %s", session->request->path,
  96. session->request->depth?session->request->depth:"null");
  97. fd = open(session->request->path, O_RDONLY, 0);
  98. if (fd >= 0)
  99. {
  100. close(fd);
  101. exit_file = 1;
  102. }
  103. dir = opendir(session->request->path);
  104. if(dir != RT_NULL)
  105. {
  106. closedir(dir);
  107. exit_file = 1;
  108. }
  109. if(!exit_file)
  110. {
  111. LOG_E("Open file(%s) is not exist.", session->request->path);
  112. session->request->result_code = 404;
  113. return WEBNET_MODULE_FINISHED;
  114. }
  115. webnet_session_printf(session, header);
  116. parent_path = session->request->path + strlen(webnet_get_root());
  117. /* output the parent. */
  118. file_stat.st_size = 0;
  119. file_stat.st_mtime = 0;
  120. file_stat.st_mode = S_IFDIR;
  121. print_propfind_element(session, parent_path, &file_stat);
  122. /* depth: 0, 1, infinity. */
  123. if( (depth == NULL || (strcmp(depth, "0") != 0) ) )
  124. {
  125. struct dirent* dirent;
  126. char *fullpath;
  127. fullpath = wn_malloc (WEBNET_PATH_MAX);
  128. rt_memset(&file_stat, 0, sizeof(struct stat));
  129. //eg. dir = opendir("/webnet/SD");
  130. dir = opendir(session->request->path);
  131. while((dirent = readdir(dir)) != NULL)
  132. {
  133. /* build full path for each file */
  134. rt_sprintf(fullpath, "%s/%s",session->request->path, dirent->d_name);
  135. str_normalize_path(fullpath);
  136. stat(fullpath, &file_stat);
  137. print_propfind_element(session, dirent->d_name,&file_stat);
  138. }
  139. closedir(dir);
  140. wn_free(fullpath);
  141. }
  142. webnet_session_printf(session,footer);
  143. return WEBNET_MODULE_FINISHED;
  144. }
  145. else if(session->request->method == WEBNET_PUT)
  146. {
  147. LOG_D("PUT %s", session->request->path);
  148. return webnet_module_put_method(session);
  149. }
  150. else if(session->request->method == WEBNET_PROPPATCH)
  151. {
  152. int proppatch_length = 0;
  153. static const char Proppatch_header[] = "HTTP/1.1 207 Multi-Status\r\n"
  154. //"Date: Mon, 07 Sep 2015 01:50:42 GMT\r\n"
  155. "Server: %s %s\r\n"
  156. "Content-Length: %d \r\n"
  157. "Content-Type: text/xml; charset=\"utf-8\"\r\n\r\n";
  158. static const char Proppatch_ex[] = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
  159. "<D:multistatus xmlns:D=\"DAV:\" xmlns:ns0=\"DAV:\">"
  160. "<D:response>"
  161. "<D:href>%s</D:href>"
  162. "<D:propstat>"
  163. "<D:prop><ns0:getlastmodified/>"
  164. "</D:prop>"
  165. "<D:status>HTTP/1.1 409 (status)</D:status>"
  166. "<D:responsedescription>"
  167. "Property is read-only.</D:responsedescription>"
  168. "</D:propstat>"
  169. "</D:response>"
  170. "</D:multistatus>";
  171. LOG_D("PROPPATCH %s", session->request->path);
  172. proppatch_length = strlen(Proppatch_ex)+strlen(session->request->path)-2;
  173. webnet_session_printf(session,Proppatch_header,WEBNET_THREAD_NAME,WEBNET_VERSION,proppatch_length);
  174. webnet_session_printf(session,Proppatch_ex,session->request->path);
  175. return WEBNET_MODULE_FINISHED;
  176. }
  177. else if (session->request->method == WEBNET_DELETE)
  178. {
  179. LOG_D("DELETE %s", session->request->path);
  180. unlink(session->request->path);
  181. webnet_session_printf(session,"HTTP/1.1 204 No Content\r\n"
  182. "Server: webnet\r\n"
  183. "Content-Length: 0\r\n"
  184. "Content-Type: text/plain\r\n\r\n");
  185. return WEBNET_MODULE_FINISHED;
  186. }
  187. else if (session->request->method == WEBNET_MKCOL)
  188. {
  189. int ret;
  190. LOG_D("MKCOL %s", session->request->path);
  191. ret = mkdir(session->request->path, 0x777);
  192. if (ret < 0)
  193. {
  194. session->request->result_code = 404;
  195. LOG_E("MKCOL mkdir error, path %s.", session->request->path);
  196. }
  197. else
  198. {
  199. LOG_D("MKCOL mkdir ok !");
  200. }
  201. webnet_session_printf(session,"HTTP/1.1 201 Created\r\n"
  202. "Server: webnet\r\n"
  203. "Content-Length: 0\r\n"
  204. "Content-Type: text/plain\r\n\r\n");
  205. return WEBNET_MODULE_FINISHED;
  206. }
  207. else if (session->request->method == WEBNET_MOVE)
  208. {
  209. char *path, *full_path;
  210. path = strstr(session->request->destination, session->request->host);
  211. if (path)
  212. {
  213. path += strlen(session->request->host);
  214. full_path = (char*) wn_malloc (WEBNET_PATH_MAX);
  215. if (full_path)
  216. {
  217. webnet_session_get_physical_path(session, path, full_path);
  218. LOG_D("Get full path, %s => %s\n", session->request->path, full_path);
  219. rename(session->request->path, full_path);
  220. wn_free(full_path);
  221. }
  222. }
  223. webnet_session_printf(session,"HTTP/1.1 200 OK\r\n"
  224. "Server: webnet\r\n"
  225. "Content-Length: 0\r\n"
  226. "Content-Type: text/plain\r\n\r\n");
  227. return WEBNET_MODULE_FINISHED;
  228. }
  229. return WEBNET_MODULE_CONTINUE;
  230. }
  231. #include <time.h>
  232. static void print_propfind_element(struct webnet_session *session, const char *uri, struct stat *file_stat)
  233. {
  234. char ctime_str[64];
  235. time_t t = file_stat->st_mtime;
  236. rt_enter_critical();
  237. strftime(ctime_str, sizeof(ctime_str), "%a, %d %b %Y %H:%M:%S GMT", localtime(&t));
  238. rt_exit_critical();
  239. //LOG_D("strftime: %s\n", ctime_str);
  240. webnet_session_printf(session,
  241. propfind_element,
  242. uri, S_ISDIR(file_stat->st_mode) ? "<d:collection/>" : "",
  243. file_stat->st_size, ctime_str);
  244. }
  245. static int put_open (struct webnet_session* session)
  246. {
  247. int fd;
  248. if(session->request->path == RT_NULL)
  249. {
  250. fd = -1;
  251. goto _exit;
  252. }
  253. fd = open(session->request->path, O_WRONLY | O_CREAT | O_TRUNC, 0);
  254. if(fd < 0)
  255. {
  256. session->session_phase = WEB_PHASE_CLOSE;
  257. fd = -1;
  258. goto _exit;
  259. }
  260. return fd;
  261. _exit:
  262. LOG_E(" %s failed ,file:%s ,line:%d",__FUNCTION__,__FILE__,__LINE__);
  263. return (int)fd;
  264. }
  265. static int put_close(struct webnet_session* session)
  266. {
  267. int fd;
  268. fd = (int)webnet_put_get_userdata(session);
  269. if (fd < 0) return 0;
  270. close(fd);
  271. return 0;
  272. }
  273. static int put_done (struct webnet_session* session)
  274. {
  275. int put_done_h_length = 0;
  276. static const char put_done_h[] = "HTTP/1.1 201 Created\r\n"
  277. "Date: Mon, 07 Sep 2015 01:50:42 GMT\r\n"
  278. "Server: %s %s\r\n"
  279. "Location: http://%s%s \r\n"
  280. "Content-Length: %d \r\n"
  281. "Content-Type: text/html; charset=ISO-8859-1\r\n\r\n";
  282. static const char put_done_t[] ="<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">"
  283. "<html><head>"
  284. "<title>";
  285. LOG_D("put_done.");
  286. put_done_h_length = strlen(put_done_t);
  287. 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);
  288. webnet_session_printf(session,put_done_t);
  289. return 0;
  290. }
  291. static int put_write(struct webnet_session* session, const void* data, rt_size_t length)
  292. {
  293. int fd;
  294. // get fd
  295. fd = (int)webnet_put_get_userdata(session);
  296. if (fd < 0) return 0;
  297. write(fd, data, length);
  298. return length;
  299. }
  300. static const void* webnet_put_get_userdata(struct webnet_session* session)
  301. {
  302. struct webnet_module_put_session *put_session;
  303. /* get put session */
  304. put_session = (struct webnet_module_put_session *)session->user_data;
  305. if (put_session == RT_NULL) return RT_NULL;
  306. return (const void*) put_session->user_data;
  307. }
  308. static void _webnet_module_put_close(struct webnet_session* session)
  309. {
  310. struct webnet_module_put_session *put_session;
  311. /* get put session */
  312. put_session = (struct webnet_module_put_session *)session->user_data;
  313. if (put_session == RT_NULL) return;
  314. /* close file */
  315. if (put_session->file_opened == 1)
  316. {
  317. put_session->entry->put_close(session);
  318. put_session->file_opened = 0;
  319. }
  320. wn_free(put_session);
  321. /* remove private data */
  322. session->user_data = 0;
  323. session->session_ops = RT_NULL;
  324. }
  325. static void _webnet_module_put_handle(struct webnet_session* session, int event)
  326. {
  327. if(session->request->method == WEBNET_PUT)
  328. {
  329. int length = 0;
  330. static int read_bytes = 0;
  331. struct webnet_module_put_session *put_session;
  332. put_session = (struct webnet_module_put_session *)session->user_data;
  333. /* read stream */
  334. length = webnet_session_read(session, (char *)session->buffer, sizeof(session->buffer) - 1);
  335. /* connection break out */
  336. if (length <= 0)
  337. {
  338. LOG_E(" %s failed ,file:%s ,line:%d\n",__FUNCTION__,__FILE__,__LINE__);
  339. /* read stream failed (connection break out), close this session */
  340. session->session_phase = WEB_PHASE_CLOSE;
  341. return;
  342. }
  343. read_bytes = read_bytes + length;
  344. session->buffer[length] = '\0';
  345. /* open file */
  346. if (put_session->file_opened == 0)
  347. {
  348. /* open file */
  349. put_session->user_data = put_session->entry->put_open(session);
  350. put_session->file_opened = 1;
  351. }
  352. //write data
  353. if (length > 0 && session->buffer != NULL)
  354. {
  355. put_session->entry->put_write(session, (char*)session->buffer, length);
  356. }
  357. if (read_bytes >= (session->request->content_length))
  358. {
  359. put_session->entry->put_done(session);
  360. _webnet_module_put_close(session);
  361. read_bytes = 0;
  362. }
  363. }
  364. }
  365. static const struct webnet_session_ops _put_ops =
  366. {
  367. _webnet_module_put_handle,
  368. _webnet_module_put_close
  369. };
  370. const struct webnet_module_put_entry put_entry_put =
  371. {
  372. put_open,
  373. put_close,
  374. put_write,
  375. put_done
  376. };
  377. int webnet_module_put_method(struct webnet_session* session)
  378. {
  379. const struct webnet_module_put_entry *entry = &put_entry_put;
  380. struct webnet_module_put_session *put_session;
  381. /* create a uploading session */
  382. put_session = (struct webnet_module_put_session*) wn_malloc (sizeof (struct webnet_module_put_session));
  383. if(put_session == RT_NULL)
  384. {
  385. LOG_E("No memory for module put session.");
  386. return WEBNET_MODULE_CONTINUE;
  387. }
  388. put_session->file_opened = 0;
  389. put_session->entry = entry;
  390. put_session->user_data = 0;
  391. /* add this put session into webnet session */
  392. session->user_data = (rt_uint32_t) put_session;
  393. /* set webnet session operations */
  394. session->session_ops = &_put_ops;
  395. return WEBNET_MODULE_FINISHED;
  396. }
  397. #endif /* WEBNET_USING_DAV */