/*
* Copyright 2017 Chris Xiong
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/*
* Get list of archives
* query parameters:
* pn=<int>: page number, defaults to 0
* pp=<int>: number of posts per page, defaults to 20
* pc: if exists, gets how many pages are there from the current pp value
* f: tag filter.
* gt: get a list of tags used by posts instead.
* qn=<filename>: query the neibouring posts of the given post.
* returned object:
* A number if pc exists denoting number of pages.
* Or the following JSON object if gf exists.
* ["tag1","tag2",...]
* Or the following JSON object if qn exists.
* {
* "prev": <last post>
* "succ": <next post>
* }
* Otherwise returns archive list in JSON:
* {
* "postsPerPage": <requested pp>
* "postsOnPage": <number of posts on this page>
* "page": <requested pn>
* "posts": [
* {
* "filename": ...
* "title": ...
* "date": ...
* "tags": ...
* },
* ...
* ]
* }
*/
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <functional>
#include <vector>
#include <map>
#include <set>
#include <string>
#include "cgiutils.hpp"
#define stripr(s) s[strlen(s)-1]=='\n'?s[strlen(s)-1]=0:0
struct post
{
std::string t,d,tg;
};
std::map<std::string,post,std::greater<std::string>> f;
char buf[65536];
int main(int argc,char** argv,char** envp)
{
struct stat idxs,cdirs;
stat("/var/www/html/blog/content/pindex",&idxs);
stat("/var/www/html/blog/content",&cdirs);
if(cdirs.st_mtim.tv_sec>idxs.st_mtim.tv_sec)
system("/var/www/html/blog/content/util/indexer > /dev/null 2> /dev/null");
QueryStrParser a;
FILE *fidx=fopen("/var/www/html/blog/content/pindex","r");
while(fgets(buf,65536,fidx))
{
stripr(buf);std::vector<std::string> v;
split(std::string(buf),'\t',v);
if(v.size()==4)
f[v[0]]=post{v[1],v[2],v[3]};
}
fclose(fidx);
HTTPHeader h;
if(a.exist("gt"))
{
std::set<std::string> s;
std::vector<std::string> v;
for(auto i=f.begin();i!=f.end();++i)
{
split(i->second.tg,',',v);
for(size_t j=0;j<v.size();++j)
s.insert("\"#"+v[j]+"\"");
}
h.appendHeader("Content-type: text/plain; charset=utf-8");
h.print();
printf("[");
auto it=s.begin();
printf("%s",it->c_str());
while(++it!=s.end())printf(",%s",it->c_str());
printf("]");
}
else
{
if(a.exist("f"))
for(auto i=f.begin();i!=f.end();)
{
std::vector<std::string> v;
split(i->second.tg,',',v);
std::set<std::string> sv=std::set<std::string>(v.begin(),v.end());
if(sv.find(a.value("f"))==sv.end()){auto t=i++;f.erase(t);}
else i++;
}
if(a.exist("pc"))
{
int pp=0;
if(!a.exist("pp"))pp=20;
else{
try{
pp=std::stoi(a.value("pp"));
}catch(std::exception e){h.setStatusCode(400);}
}
if(!pp)h.setStatusCode(400);
h.appendHeader("Content-type: text/plain; charset=utf-8");
h.print();
printf("%lu\n",f.size()/pp+((f.size()%pp)?1:0));
return 0;
}
else if(a.exist("qn"))
{
if(f.find(a.value("qn"))==f.end())
h.setStatusCode(400);
h.print();if(h.statusCode()>=400)return 0;
auto i=f.find(a.value("qn"));
std::string pr="",sc="";
auto t=i;if(t!=f.begin())pr=(--t)->first;
t=i;if(!(++t==f.end()))sc=t->first;
printf("{\"prev\":\"%s\",\"succ\":\"%s\"}",sc.c_str(),pr.c_str());
return 0;
}
else
{
unsigned pp=20,pn=0;
if(!a.exist("pp"))pp=20;
else{
try{
pp=std::stoi(a.value("pp"));
}catch(std::exception e){h.setStatusCode(400);}
}
if(!a.exist("pn"))pn=0;
else{
try{
pn=std::stoi(a.value("pn"));
}catch(std::exception e){h.setStatusCode(400);}
}
if(!pp)h.setStatusCode(400);
if(pn>=f.size()/pp+((f.size()%pp)?1:0))h.setStatusCode(400);
int rpp=(pn!=f.size()/pp+((f.size()%pp)?1:0)-1)?pp:
f.size()-pp*(f.size()/pp+((f.size()%pp)?1:0)-1);
h.appendHeader("Content-type: text/plain; charset=utf-8");
h.print();
if(h.statusCode()>=400)return 0;
puts("{");
printf("\t\"postsPerPage\":%d,\n",pp);
printf("\t\"postsOnPage\":%d,\n",rpp);
printf("\t\"page\":%d,\n",pn);
puts("\t\"posts\": [");
auto it=f.begin();std::advance(it,pn*pp);
for(int i=0;i<rpp-1;++i)
{
puts("\t\t{");
printf("\t\t\t\"filename\":\"%s\",\n",it->first.c_str());
printf("\t\t\t\"title\":\"%s\",\n",it->second.t.c_str());
printf("\t\t\t\"date\":\"%s\",\n",it->second.d.c_str());
printf("\t\t\t\"tags\":\"%s\"\n",it->second.tg.c_str());
puts("\t\t},");++it;
}
puts("\t\t{");
printf("\t\t\t\"filename\":\"%s\",\n",it->first.c_str());
printf("\t\t\t\"title\":\"%s\",\n",it->second.t.c_str());
printf("\t\t\t\"date\":\"%s\",\n",it->second.d.c_str());
printf("\t\t\t\"tags\":\"%s\"\n",it->second.tg.c_str());
puts("\t\t}");
puts("\t]");
puts("}");
}
}
return 0;
}