| 713 |
714 |
| 714 ws.on_message = ws_msghandler; |
715 ws.on_message = ws_msghandler; |
| 715 return http_handle_websocket(sn, rq, &ws); |
716 return http_handle_websocket(sn, rq, &ws); |
| 716 } |
717 } |
| 717 |
718 |
| |
719 |
| |
720 // TODO: maybe move these util functions to another file or library |
| |
721 static char* util_size_str2(const CxAllocator *a, WSBool iscollection, uint64_t contentlength, uint64_t dimension, int precision) { |
| |
722 char *str = cxMalloc(a, 16); |
| |
723 uint64_t size = contentlength; |
| |
724 |
| |
725 if(iscollection) { |
| |
726 str[0] = '\0'; // currently no information for collections |
| |
727 } else if(dimension < 0x400) { |
| |
728 snprintf(str, 16, "%" PRIu64 " bytes", size); |
| |
729 } else if(dimension < 0x100000) { |
| |
730 float s = (float)size/0x400; |
| |
731 int diff = (s*100 - (int)s*100); |
| |
732 if(diff > 90) { |
| |
733 diff = 0; |
| |
734 s += 0.10f; |
| |
735 } |
| |
736 if(dimension < 0x2800 && diff != 0) { |
| |
737 // size < 10 KiB |
| |
738 snprintf(str, 16, "%.*f KiB", precision, s); |
| |
739 } else { |
| |
740 snprintf(str, 16, "%.0f KiB", s); |
| |
741 } |
| |
742 } else if(dimension < 0x40000000) { |
| |
743 float s = (float)size/0x100000; |
| |
744 int diff = (s*100 - (int)s*100); |
| |
745 if(diff > 90) { |
| |
746 diff = 0; |
| |
747 s += 0.10f; |
| |
748 } |
| |
749 if(dimension < 0xa00000 && diff != 0) { |
| |
750 // size < 10 MiB |
| |
751 snprintf(str, 16, "%.*f MiB", precision, s); |
| |
752 } else { |
| |
753 size /= 0x100000; |
| |
754 snprintf(str, 16, "%.0f MiB", s); |
| |
755 } |
| |
756 } else if(dimension < 0x1000000000ULL) { |
| |
757 float s = (float)size/0x40000000; |
| |
758 int diff = (s*100 - (int)s*100); |
| |
759 if(diff > 90) { |
| |
760 diff = 0; |
| |
761 s += 0.10f; |
| |
762 } |
| |
763 if(dimension < 0x280000000 && diff != 0) { |
| |
764 // size < 10 GiB |
| |
765 snprintf(str, 16, "%.*f GiB", precision, s); |
| |
766 } else { |
| |
767 size /= 0x40000000; |
| |
768 snprintf(str, 16, "%.0f GiB", s); |
| |
769 } |
| |
770 } else { |
| |
771 size /= 1024; |
| |
772 float s = (float)size/0x40000000; |
| |
773 int diff = (s*100 - (int)s*100); |
| |
774 if(diff > 90) { |
| |
775 diff = 0; |
| |
776 s += 0.10f; |
| |
777 } |
| |
778 if(dimension < 0x280000000 && diff != 0) { |
| |
779 // size < 10 TiB |
| |
780 snprintf(str, 16, "%.*f TiB", precision, s); |
| |
781 } else { |
| |
782 size /= 0x40000000; |
| |
783 snprintf(str, 16, "%.0f TiB", s); |
| |
784 } |
| |
785 } |
| |
786 return str; |
| |
787 } |
| |
788 |
| |
789 static char* util_size_str(const CxAllocator *a, WSBool iscollection, uint64_t contentlength) { |
| |
790 return util_size_str2(a, iscollection, contentlength, contentlength, 1); |
| |
791 } |
| |
792 |
| |
793 static char* util_date_str(const CxAllocator *a, time_t tm) { |
| |
794 struct tm t; |
| |
795 struct tm n; |
| |
796 time_t now = time(NULL); |
| |
797 #ifdef _WIN32 |
| |
798 memcpy(&t, localtime(&tm), sizeof(struct tm)); |
| |
799 memcpy(&n, localtime(&now), sizeof(struct tm)); |
| |
800 #else |
| |
801 localtime_r(&tm, &t); |
| |
802 localtime_r(&now, &n); |
| |
803 #endif /* _WIN32 */ |
| |
804 char *str = cxMalloc(a, 16); |
| |
805 if(t.tm_year == n.tm_year) { |
| |
806 strftime(str, 16, "%b %d %H:%M", &t); |
| |
807 } else { |
| |
808 strftime(str, 16, "%b %d %Y", &t); |
| |
809 } |
| |
810 return str; |
| |
811 } |
| |
812 |
| |
813 static int cmp_file_type_name(IndexEntry *a, IndexEntry *b) { |
| |
814 if(a->isdir != b->isdir) { |
| |
815 return a->isdir ? -1 : 1; |
| |
816 } |
| |
817 return strcmp(a->name, b->name); |
| |
818 } |
| |
819 |
| 718 int service_index(pblock *pb, Session *sn, Request *rq) { |
820 int service_index(pblock *pb, Session *sn, Request *rq) { |
| 719 //printf("service_index\n"); |
821 //printf("service_index\n"); |
| |
822 const CxAllocator *a = pool_allocator(sn->pool); |
| 720 |
823 |
| 721 char *path = pblock_findkeyval(pb_key_path, rq->vars); |
824 char *path = pblock_findkeyval(pb_key_path, rq->vars); |
| 722 char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb); |
825 char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb); |
| 723 |
|
| 724 cxstring r_uri = cx_str(uri); |
|
| 725 |
826 |
| 726 // open the file |
827 // open the file |
| 727 VFSContext *vfs = vfs_request_context(sn, rq); |
828 VFSContext *vfs = vfs_request_context(sn, rq); |
| 728 VFS_DIR dir = vfs_opendir(vfs, path); |
829 VFS_DIR dir = vfs_opendir(vfs, path); |
| 729 if(!dir) { |
830 if(!dir) { |
| 731 } |
832 } |
| 732 |
833 |
| 733 sbuf_t *out = sbuf_new(1024); // output buffer |
834 sbuf_t *out = sbuf_new(1024); // output buffer |
| 734 |
835 |
| 735 // write html header |
836 // write html header |
| 736 sbuf_puts(out, "<html>\n<head>\n<title>Index of "); |
837 sbuf_puts(out, "<!DOCTYPE html>\n<html>\n<head>\n<title>Index of "); |
| 737 sbuf_puts(out, uri); |
838 sbuf_puts(out, uri); |
| 738 sbuf_puts(out, "</title>\n</head><body>\n<h1>Index of "); |
839 sbuf_puts(out, "</title>\n"); |
| |
840 sbuf_puts(out, "<style>\n"); |
| |
841 sbuf_puts(out, "th { text-align: left; }\n"); |
| |
842 sbuf_puts(out, "td { padding-right: 1em; }\n"); |
| |
843 sbuf_puts(out, "</style>\n"); |
| |
844 sbuf_puts(out, "</head><body>\n<h1>Index of "); |
| 739 sbuf_puts(out, uri); |
845 sbuf_puts(out, uri); |
| 740 sbuf_puts(out, "</h1><hr>\n\n"); |
846 sbuf_puts(out, "</h1><hr>\n\n"); |
| 741 |
847 |
| 742 // list directory |
848 CxList *files = cxLinkedListCreate(a, (cx_compare_func)cmp_file_type_name, sizeof(IndexEntry)); |
| |
849 if(!files) { |
| |
850 return REQ_ABORTED; |
| |
851 } |
| |
852 |
| |
853 // read directory at store entries in the files list |
| |
854 int ret = REQ_PROCEED; |
| 743 VFS_ENTRY f; |
855 VFS_ENTRY f; |
| 744 while(vfs_readdir(dir, &f)) { |
856 while(vfs_readdir_stat(dir, &f)) { |
| 745 cxstring filename = cx_str(f.name); |
857 IndexEntry entry; |
| 746 |
858 entry.name = pool_strdup(sn->pool, f.name); |
| |
859 if(!entry.name) { |
| |
860 ret = REQ_ABORTED; |
| |
861 break; |
| |
862 } |
| |
863 if(f.stat_errno == 0) { |
| |
864 entry.isdir = S_ISDIR(f.stat.st_mode); |
| |
865 entry.size = (size_t)f.stat.st_size; |
| |
866 entry.size_str = util_size_str(a, entry.isdir, entry.size); |
| |
867 entry.lastmodified = util_date_str(a, f.stat.st_mtime); |
| |
868 } else { |
| |
869 entry.isdir = 0; |
| |
870 entry.lastmodified = NULL; |
| |
871 entry.size_str = NULL; |
| |
872 entry.size = 0; |
| |
873 } |
| |
874 |
| |
875 if(cxListAdd(files, &entry)) { |
| |
876 ret = REQ_ABORTED; |
| |
877 break; |
| |
878 } |
| |
879 } |
| |
880 |
| |
881 // generate html output |
| |
882 sbuf_puts(out, "<table>\n<tr><th>Type</th><th>Name</th><th>Size</th><th>Last Modified</th></tr>\n"); |
| |
883 cxListSort(files); |
| |
884 CxIterator i = cxListIterator(files); |
| |
885 cx_foreach(IndexEntry *, entry, i) { |
| |
886 sbuf_puts(out, "<tr>\n"); |
| |
887 |
| |
888 sbuf_puts(out, "<td>"); |
| |
889 sbuf_puts(out, entry->isdir ? "[DIR]" : "[FILE]"); |
| |
890 sbuf_puts(out, "</td>"); |
| |
891 |
| |
892 sbuf_puts(out, "<td>"); |
| 747 sbuf_puts(out, "<a href=\""); |
893 sbuf_puts(out, "<a href=\""); |
| 748 sbuf_append(out, r_uri); |
894 sbuf_puts(out, entry->name); |
| 749 sbuf_append(out, filename); |
|
| 750 sbuf_puts(out, "\">"); |
895 sbuf_puts(out, "\">"); |
| 751 sbuf_append(out, filename); |
896 sbuf_puts(out, entry->name); |
| 752 sbuf_puts(out, "</a><br>\n"); |
897 sbuf_puts(out, "</a>"); |
| 753 } |
898 sbuf_puts(out, "</td>"); |
| 754 |
899 |
| 755 sbuf_puts(out, "\n</body>\n</html>\n"); |
900 sbuf_puts(out, "<td>"); |
| |
901 if(entry->size_str) { |
| |
902 sbuf_puts(out, entry->size_str); |
| |
903 } |
| |
904 sbuf_puts(out, "</td>"); |
| |
905 |
| |
906 sbuf_puts(out, "<td>"); |
| |
907 if(entry->size_str) { |
| |
908 sbuf_puts(out, entry->lastmodified); |
| |
909 } |
| |
910 sbuf_puts(out, "</td>"); |
| |
911 |
| |
912 sbuf_puts(out, "</tr>\n"); |
| |
913 } |
| |
914 |
| |
915 sbuf_puts(out, "</table>\n</body>\n</html>\n"); |
| 756 |
916 |
| 757 // send stuff to client |
917 // send stuff to client |
| 758 pblock_removekey(pb_key_content_type, rq->srvhdrs); |
918 if(ret == REQ_PROCEED) { |
| 759 pblock_kvinsert(pb_key_content_type, "text/html", 9, rq->srvhdrs); |
919 pblock_removekey(pb_key_content_type, rq->srvhdrs); |
| 760 pblock_nninsert("content-length", out->length, rq->srvhdrs); |
920 pblock_kvinsert(pb_key_content_type, "text/html; charset=utf-8", 24, rq->srvhdrs); |
| 761 protocol_status(sn, rq, 200, NULL); |
921 pblock_nninsert("content-length", out->length, rq->srvhdrs); |
| 762 http_start_response(sn, rq); |
922 protocol_status(sn, rq, 200, NULL); |
| 763 |
923 http_start_response(sn, rq); |
| 764 net_write(sn->csd, out->ptr, out->length); |
924 |
| |
925 net_write(sn->csd, out->ptr, out->length); |
| |
926 } |
| 765 |
927 |
| 766 // close |
928 // close |
| 767 vfs_closedir(dir); |
929 vfs_closedir(dir); |
| 768 sbuf_free(out); |
930 sbuf_free(out); |
| 769 |
931 |
| 770 return REQ_PROCEED; |
932 return ret; |
| 771 } |
933 } |
| 772 |
934 |
| 773 int send_options(pblock *pb, Session *sn, Request *rq) { |
935 int send_options(pblock *pb, Session *sn, Request *rq) { |
| 774 char *allow = "HEAD, GET, PUT, DELETE, TRACE, OPTIONS, MOVE, COPY, " |
936 char *allow = "HEAD, GET, PUT, DELETE, TRACE, OPTIONS, MOVE, COPY, " |
| 775 "PROPFIND, PROPPATCH, MKCOL, LOCK, UNLOCK, ACL, REPORT"; |
937 "PROPFIND, PROPPATCH, MKCOL, LOCK, UNLOCK, ACL, REPORT"; |