#include <string>
#include <valarray>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <err.h>
#include <map>
#include <regex>
#include <dirent.h>
#include <list>
#include <numeric>
#include <thread>
#include <arpa/inet.h>
#include <cctype>
#define threadNum 100
auto UrlEncodeTableGenerate()
{
char html5[256] = {0};
for (auto i = 0; i < 256; i++) html5[i] = isalnum(i) || i == '*' || i == '-' || i == '.' || i == '_'
? i
: (i == ' ')
? '+'
: 0;
return html5;
}
const auto UrlEncodeTable = UrlEncodeTableGenerate();
int FileExists(const char* path)
{
struct stat sb{};
return !stat(path, &sb) && S_ISREG(sb.st_mode);
}
int DirectoryExists(const char* path)
{
struct stat sb{};
return !stat(path, &sb) && S_ISDIR(sb.st_mode);
}
uint64_t FileSize(const char* path)
{
struct stat sb{};
stat(path, &sb);
return sb.st_size;
}
std::string encode(const char* s, uint16_t len)
{
auto enc = new char[len * 3];
for (; *s; s++)
{
if (UrlEncodeTable[*s]) sprintf(enc, "%c", UrlEncodeTable[*s]);
else sprintf(enc, "%%%02X", *s);
while (*++enc);
}
auto r = std::string(enc);
delete[] enc;
return r;
}
std::string UrlDecode(const char* s, uint16_t len)
{
char* dec = new char[len + 1];
const char* end = s + strlen(s);
int c;
for (char* o = dec; s <= end; o++)
{
c = *s++;
if (c == '+') c = ' ';
else if (c == '%')
{
*s++;
*s++;
sscanf(s - 2, "%2x", &c);
}
*o = c;
}
auto r = std::string(dec, strlen(dec));
delete[] dec;
return r;
}
void GetFiles(const char* path, std::list<std::string>& dirs, std::list<std::string>& files)
{
struct dirent* dent;
struct stat st;
char fn[FILENAME_MAX] = {0};
auto len = strlen(path);
if (len >= FILENAME_MAX - 1) err(EXIT_FAILURE, "Filename too long");
strcpy(fn, path);
if (fn[len - 1] != '/')fn[len++] = '/';
DIR* dir = opendir(path);
if (!dir) err(EXIT_FAILURE, "Can't open %s", path);
while ((dent = readdir(dir)))
{
if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) continue;
strncpy(fn + len, dent->d_name, FILENAME_MAX - len);
if (lstat(fn, &st) == -1)
{
warn("Can't stat %s", fn);
continue;
}
auto _fn = std::string(fn);
if (S_ISDIR(st.st_mode))
dirs.push_back(
"<a href=\"" + UrlDecode(_fn.c_str(), _fn.length()) + "/\">" + _fn.substr(len, _fn.length() - len) +
"/</a><br/>");
else if (std::regex_match(_fn, std::regex(".+\\.(jpg|png|JPG|PNG)")))
files.push_back("<li><img src=\"" + UrlDecode(_fn.c_str(), _fn.length()) + "\"/></li>");
}
if (dir) closedir(dir);
}
#define HttpHead(value, http, sm) \
std::regex_search(http, sm, std::regex(value": {0,1}.+?\\r{0,1}\\n"));\
const auto (value) = std::regex_replace((sm)[0].str(), std::regex("("#value": {0,1}|\\r{0,1}\\n)"), "")
#define HttpUrl(url, http, sm)\
std::regex_search(http, sm, std::regex("(POST|GET) .+? HTTP"));\
const auto (url) = std::regex_replace((sm)[0].str(), std::regex("(POST |GET | HTTP|)"), "")
#define HttpHtml(html) \
("HTTP/1.1 200 OK\r\n\
Content-length:" + std::to_string((html).length()) + "\r\n\
Content-Type: text/html; charset=UTF-8\r\n\r\n" + (html))
uint64_t HttpPicture(const char* path, void* buf, uint64_t& size)
{
const auto pathLen = strlen(path);
const auto head = "HTTP/1.1 200 OK\r\nContent-length:" +
std::to_string(size) +
"\r\nContent-Type: image/" +
(path[pathLen - 3] == 'p' ? "png" : "jpeg") +
"\r\n\r\n";
memcpy(buf, head.c_str(), head.length());
const auto fp = fopen(path, "rb");
fread(buf + head.length(), sizeof(uint8_t), size, fp);
fclose(fp);
return size + head.length();
}
void IndexOf(const char* path, std::string& response)
{
auto dirs = std::list<std::string>();
auto files = std::list<std::string>();
GetFiles(path, dirs, files);
const auto dirList = dirs.size()
? std::accumulate(dirs.begin(), dirs.end(), std::string(""), [&](auto& a, auto& b)
{
return a + b;
})
: " <br/>";
const auto filesList = files.size()
? std::accumulate(files.begin(), files.end(), std::string(""), [&](auto& a, auto& b)
{
return a + b;
})
: "";
const auto html = std::string(" <!DOCTYPE html>") +
"<html>" +
"<head><title>Index of " + path + "</title>" +
R"(<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>)" +
R"(<style type="text/css">
img{
max-width:260px;
max-height:260px;
}
.ex{
width:90%;
background-color: #FFF;
height: 90%;
max-width:unset;
max-height:unset;
}
li{
width:260px;
height:260px;
float:left;
margin-left:10px;
margin-top:10px;
list-style-type:none;
text-align:center;
}
#TempContainer {
position: absolute;
z-index: 101;
margin-right: 0px;
margin-left: 0px;
text-align: center;
width: 99%;
cursor: pointer;
}
</style>)" +
R"(<script type="text/javascript" src="https://code.jquery.com/jquery-3.4.1.min.js"></script>)" +
R"(<script type="text/javascript">
$(document).ready(function(e) {
var ImgsTObj = $('img');
if(ImgsTObj){
$.each(ImgsTObj,function(){
$(this).click(function(){
var currImg = $(this);
var TempContainer = $('<div class=TempContainer></div>');
with(TempContainer){
appendTo("body");
html('<img class="ex" border=0 src="' + currImg.attr('src') + '">');
}
TempContainer.click(function(){
$(this).remove();
});
});
});
}
else{
return false;
}
});
</script>)" +
"</head>" +
"<body>" +
"<h1>Index of " + path + "</h1><hr>" +
dirList + (dirs.size() ? "<hr>" : "") +
"<div><ul>" +
filesList +
"</ul></div></body>" +
"</html>";
response = HttpHtml(html);
}
bool CheckUrl(const std::string url, const char* path)
{
std::regex_replace(url, std::regex("\\.\\."), "");
return !memcmp(url.c_str(), path, strlen(path) - 1);
}
void Index(const char* path, const int port)
{
auto one = 1;
struct sockaddr_in svrAddr, cliAddr;
socklen_t sinLen = sizeof(cliAddr);
auto sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock < 0) err(EXIT_FAILURE, "Can't open socket");
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
svrAddr.sin_family = AF_INET;
svrAddr.sin_addr.s_addr = INADDR_ANY;
svrAddr.sin_port = htons(port);
if (bind(sock, (struct sockaddr *)&svrAddr, sizeof(svrAddr)) == -1)
{
close(sock);
err(1, "Can't bind");
}
listen(sock, threadNum);
std::valarray<std::thread> pool(threadNum);
std::generate(begin(pool), end(pool), [&]()
{
return std::thread([&]()
{
while (true)
{
const auto clientFd = accept(sock, (struct sockaddr *)&cliAddr, &sinLen);
char buf[1024] = {0};
auto http = std::string();
int r = read(clientFd, buf, 1024);
http.append(buf, r);
printf("%s:%d===================>\n%s\n", inet_ntoa(cliAddr.sin_addr), ntohs(cliAddr.sin_port),
http.c_str());
std::smatch sm;
HttpUrl(_url, http, sm);
auto url = UrlDecode(_url.c_str(), _url.length());
const auto urlStatus = CheckUrl(url, path);
if (urlStatus && DirectoryExists(url.c_str()))
{
auto response = std::string();
IndexOf(url.c_str(), response);
send(clientFd, response.c_str(), response.length(), 0);
}
else if (urlStatus && FileExists(url.c_str()))
{
auto size = FileSize(url.c_str());
auto buf = new uint8_t[size + 1024];
auto bufLen = HttpPicture(url.c_str(), buf, size);
send(clientFd, buf, bufLen, 0);
delete[] buf;
}
else
{
auto response = std::string();
IndexOf(path, response);
send(clientFd, response.c_str(), response.length(), 0);
}
close(clientFd);
}
});
});
for (auto& t : pool) t.join();
}
int main(const int argc, char* argv[])
{
if (argc != 3)
{
fprintf(stderr, "%s IndexPath Port\n", argv[0]);
exit(EXIT_FAILURE);
}
Index(argv[1], strtol(argv[2], &argv[2], 10));
}