/* * kakumei_posts.c * * Posts management * * Author: Dario Rodriguez dario@softhome.net * This program is licensed under the terms of the Affero GPL v1+ */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <time.h> #include "loglib.h" #include "kakumei.h" #include "kakumei_posts.h" #define MAXPOSTS 2000000 int post_new(kakumei *ka, char *user, char *title, char *text) { int last; int num; char filename[1024]; char dirname[1024]; FILE *f; struct stat st; if(ka==NULL || user==NULL || user[0]=='\0' || text==NULL || text[0]=='\0') { log_write("POST","sanity check error\n"); return(-1); } last=post_last(ka); if(last==-1) last=0; /* post number starts on 1 (last+1)*/ filename[0]='\0'; for(num=last+1;num<MAXPOSTS;num++) { snprintf(filename,sizeof(filename),"%s/%04i/%04i.json",POSTSDIR,num/10000,num%10000); filename[sizeof(filename)-1]='\0'; if(stat(filename,&st)!=0) break; /* this one is ok: file doesn't exist */ } if(filename[0]=='\0' || num>=MAXPOSTS) { log_write("POST","couldn't generate filename or too many posts\n"); return(-1); } /* create dir structure */ mkdir(DATADIR,0700); mkdir(POSTSDIR,0700); snprintf(dirname,sizeof(dirname),"%s/%04i",POSTSDIR,num/10000); dirname[sizeof(dirname)-1]='\0'; mkdir(dirname,0700); /* save the new post */ if((f=fopen(filename,"w"))==NULL) { log_write("POST","couldn't save post\n"); return(-1); } fprintf(f,"{\n \"author\":\"%s\",\n",user); fprintf(f," \"date\":%li,\n",(long)time(NULL)); fprintf(f," \"title\":\"%s\",\n",title); fprintf(f," \"text\":\"%s\",\n",text); fprintf(f," \"comments\":[\n]}\n"); fclose(f),f=NULL; /* save the "last" mark */ snprintf(filename,sizeof(filename)-1,"%s/%s",POSTSDIR,"last"); filename[sizeof(filename)-1]='\0'; if((f=fopen(filename,"w"))!=NULL) { fprintf(f,"%i",num); fclose(f),f=NULL; } return(0); } int post_last(kakumei *ka) { char readbuf[128]; char filename[1024]; int fd; snprintf(filename,sizeof(filename)-1,"%s/%s",POSTSDIR,"last"); filename[sizeof(filename)-1]='\0'; if((fd=open(filename,O_RDONLY))==-1) { log_write("POST","couldn't get last post number\n"); return(-1); } memset(readbuf,0,sizeof(readbuf)); read(fd,readbuf,sizeof(readbuf)-1); close(fd),fd=-1; return(atoi(readbuf)); } int post_get(kakumei *ka, int num, char *filename, int filenamesize) { struct stat st; if(ka==NULL || num<0 || filename==NULL || filenamesize<=0) { log_write("POST","Sanity check error\n"); return(-1); } snprintf(filename,filenamesize,"%s/%04i/%04i.json",POSTSDIR,num/10000,num%10000); filename[filenamesize-1]='\0'; if(stat(filename,&st)!=0) { log_write("POST","Post file doesn't exist\n"); return(-1); /* file doesn't exist */ } return(0); } int post_addcomment(kakumei *ka, int postnum, char *user, char *text) { char filename[1024]; char buf[16]; FILE *f; int off; if(ka==NULL || postnum<0 || user==NULL || text==NULL) { log_write("POST","Sanity check error\n"); return(-1); } if(post_get(ka,postnum,filename,sizeof(filename))!=0) return(-1); if((f=fopen(filename,"r+"))==NULL) { log_write("POST","Couldn't open post file for appending\n"); return(-1); } fseek(f,-5,SEEK_END); if(fread(buf,1,5,f)!=5) { fclose(f),f=NULL; log_write("POST","Post file contents are too small\n"); return(-1); } off=(buf[4]=='\n')?1:0; if(buf[4-off]!='}' || buf[3-off]!=']' || buf[2-off]!='\n') { fclose(f),f=NULL; log_write("POST","Post file contents are corrupted\n"); return(-1); } fseek(f,-4-off,SEEK_END); fprintf(f,"%c%s\n { \"author\":\"%s\",\"date\":%li,\"text\":\"%s\"}\n]}", buf[1-off],(buf[1-off]!='[')?",":"", user,(long)time(NULL),text); fclose(f),f=NULL; return(0); }