summaryrefslogblamecommitdiff
path: root/blog/sbs_2/cgi-src/get-archive-list.cpp
blob: 28f316dc9a94a0a3cedd5d9fecdeda9aa1e7eace (plain) (tree)







































































































































































































                                                                                          
/*
 * 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;
}