/* * kakumei.c * * Private group web. * * Author: Dario Rodriguez dario@softhome.net * This progran is licensed under the terms of the Affero GPL v1+ */ #include #include #include #include #include #include #include #include #include "gen_res.h" #include "socklib.h" #include "loglib.h" #include "webkernel.h" #include "kakumei.h" #include "kakumei_session.h" #include "kakumei_pass.h" static int signal_init(int signum, void (*fn)(int)); static void sigint(int signum); volatile int sigint_flag=0; int kakumei_inviteexists(kakumei *ka, char *invite); int kakumei_invitedel(kakumei *ka, char *invite); wk_action callback_http(wk *web, int connid, wk_uri *uri, void *userptr); wk_action http_login(wk *web, int connid, wk_uri *uri, void *userptr); wk_action http_newuser(wk *web, int connid, wk_uri *uri, void *userptr); int main(int argc, char *argv[]) { int port; int timeout=500; kakumei *ka,kastore; memset(&kastore,0,sizeof(kastore)); ka=&kastore; if(argc!=2) { printf("Syntax: %s port\n",argv[0]); return(1); } port=atoi(argv[1]); if((ka->ssel=sselect_init())==NULL) { log_write("INIT","ERROR: insufficient memory"); return(1); } if((ka->web=wk_init(port,ka->ssel,NULL,callback_http,NULL,NULL,ka))==NULL) { sselect_free(ka->ssel),ka->ssel=NULL; log_write("INIT","ERROR: couldn't init web server"); return(2); } sigint_flag=0; signal_init(SIGINT,sigint); log_write("INIT","Server initialized, waiting connections..."); while(!sigint_flag) { sselect_wait(ka->ssel,timeout); wk_service(ka->web); } wk_free(ka->web),ka->web=NULL; sselect_free(ka->ssel),ka->ssel=NULL; log_write("FINI","SIGINT detected, exiting..."); return(0); } int kakumei_uservalid(kakumei *ka, char *username) { if(username==NULL) return(-1); if(strchr(username,'/')!=NULL) return(-1); if(strcmp(username,".")==0 || strcmp(username,"..")==0) return(-1); return(0); } int kakumei_userexists(kakumei *ka, char *username) { char filename[1024]; struct stat st; if(kakumei_uservalid(ka,username)!=0) return(-1); snprintf(filename,sizeof(filename),"%s/%s/passwd",USERSDIR,username); filename[sizeof(filename)-1]='\0'; if(stat(filename,&st)!=0 || !S_ISREG(st.st_mode)) return(-1); return(0); } int kakumei_inviteexists(kakumei *ka, char *invite) { char filename[1024]; struct stat st; if(invite==NULL) return(-1); if(strchr(invite,'/')!=NULL) return(-1); if(strcmp(invite,".")==0 || strcmp(invite,"..")==0) return(-1); snprintf(filename,sizeof(filename),"%s/%s",INVITESDIR,invite); filename[sizeof(filename)-1]='\0'; if(stat(filename,&st)!=0 || !S_ISREG(st.st_mode)) return(-1); return(0); } int kakumei_invitedel(kakumei *ka, char *invite) { char filename[1024]; if(kakumei_inviteexists(ka,invite)!=0) return(-1); /* doesn't exist */ snprintf(filename,sizeof(filename),"%s/%s",INVITESDIR,invite); filename[sizeof(filename)-1]='\0'; unlink(filename); if(kakumei_inviteexists(ka,invite)==0) return(-1); /* couldn't remove invite */ return(0); } wk_action callback_http(wk *web, int connid, wk_uri *uri, void *userptr) { kakumei *ka=(kakumei *)userptr; resindex *res; char partialpath[1024]; char *ptr; struct { char *name; } whitelist[]={{"/index.html"}, {"/newuser.html"} }; int len; int ishtml; int whitelisted; int validsession; char session[SESSIONSIZE]; char user[MAXUSERSIZE+1]; int i; if(ka==NULL) return(wkact_finished); /* log without passwords */ if(memcmp(uri->path,"/login?",7)==0) log_write("HTTP","Request: /login?..."); else if(memcmp(uri->path,"/newuser?",9)==0) log_write("HTTP","Request: /newuser?..."); else log_write("HTTP","Request: %s",uri->path); /* extract the name */ strncpy(partialpath,uri->path,sizeof(partialpath)-1); partialpath[sizeof(partialpath)-1]='\0'; if(strcmp(uri->path,"/")==0) strcpy(partialpath,"/index.html"); if((ptr=strchr(partialpath,'?'))!=NULL) *ptr='\0'; /* check whitelist */ len=strlen(partialpath); ishtml=(len>5 && strcmp(partialpath+len-5,".html")==0)?1:0; if(ishtml) { for(whitelisted=0,i=0;i<(sizeof(whitelist)/sizeof(whitelist[0]));i++) { if(strcmp(partialpath,whitelist[i].name)==0) { whitelisted=1; break; } } } else whitelisted=1; if(wk_uri_copyvar(uri,"s",session,sizeof(session))==NULL) session[0]='\0'; user[0]='\0'; validsession=(session_check(ka,session,user,sizeof(user))!=NULL)?1:0; /* serve the page */ if(partialpath[0]=='/' && (res=res_find(resindexdata,partialpath+1))!=NULL) { if(whitelisted || validsession) { log_write("HTTP","Serving in-memory file %s",partialpath+1); wk_serve_buffer_as_file(web,connid,res->data,res->len,mime_getdefault(res->name,"application/octet-stream")); return(wkact_finished); } else if((res=res_find(resindexdata,"index.html"))!=NULL) { log_write("HTTP","Not allowed page, redirecting to login"); wk_serve_buffer_as_file(web,connid,res->data,res->len,mime_getdefault(res->name,"application/octet-stream")); return(wkact_finished); } else { log_write("EINT","%s:%i",__FILE__,__LINE__); wk_serve_error(web,connid,wkerr_internal); return(wkact_finished); /* internal error */ } } /* check for actions */ if(memcmp(uri->path,"/login?",7)==0) { return(http_login(web,connid,uri,userptr)); } else if(memcmp(uri->path,"/newuser?",9)==0) { return(http_newuser(web,connid,uri,userptr)); } /* not found */ log_write("HTTP","URI not found: %s",uri->path); wk_serve_error(web,connid,wkerr_notfound); return(wkact_finished); } static int signal_init(int signum, void (*fn)(int)) { struct sigaction sa; sa.sa_handler=fn; sigemptyset(&sa.sa_mask); sa.sa_flags=0; return(sigaction(signum,&sa,0)); } static void sigint(int signum) { sigint_flag=1; } /* implement the "XmlHttpRequest"s (the reply is the new page to load) */ wk_action http_login(wk *web, int connid, wk_uri *uri, void *userptr) { char u[MAXUSERSIZE+1],p[MAXPASSWDSIZE+1]; char reply[1024]; char session[SESSIONSIZE]; kakumei *ka=(kakumei *)userptr; if(web==NULL || connid<0 || uri==NULL || ka==NULL) { log_write("EINT","%s:%i",__FILE__,__LINE__); wk_serve_error(web,connid,wkerr_internal); return(wkact_finished); /* internal error */ } if(wk_uri_copyvar(uri,"u",u,sizeof(u))==NULL) u[0]='\0'; if(wk_uri_copyvar(uri,"p",p,sizeof(p))==NULL) p[0]='\0'; /* check for invitation */ if(strcmp(u,INVITATIONUSER)==0 && kakumei_inviteexists(ka,p)==0) { /* valid invitation */ snprintf(reply,sizeof(reply),"/newuser.html?i=%s",p); reply[sizeof(reply)-1]='\0'; wk_serve_buffer_as_file(web,connid,reply,strlen(reply),"text/plain"); log_write("LGIN","Reply: %s",reply); return(wkact_finished); } else if(pass_check(ka,u,p)==0 && session_new(ka,u,session,sizeof(session))!=NULL) { /* valid login */ snprintf(reply,sizeof(reply),"/posts.html?s=%s",session); reply[sizeof(reply)-1]='\0'; wk_serve_buffer_as_file(web,connid,reply,strlen(reply),"text/plain"); log_write("LGIN","Reply: %s",reply); return(wkact_finished); } log_write("LGIN","Serving error"); wk_serve_error(web,connid,wkerr_internal); return(wkact_finished); } wk_action http_newuser(wk *web, int connid, wk_uri *uri, void *userptr) { char u[MAXUSERSIZE+1],p[MAXPASSWDSIZE+1],i[MAXPASSWDSIZE+1]; char reply[1024]; char session[SESSIONSIZE]; kakumei *ka=(kakumei *)userptr; if(web==NULL || connid<0 || uri==NULL || ka==NULL) { log_write("EINT","%s:%i",__FILE__,__LINE__); return(wkact_finished); /* internal error */ } /* get vars */ if(wk_uri_copyvar(uri,"i",i,sizeof(i))==NULL) i[0]='\0'; if(wk_uri_copyvar(uri,"u",u,sizeof(u))==NULL) u[0]='\0'; if(wk_uri_copyvar(uri,"p",p,sizeof(p))==NULL) p[0]='\0'; /* check validity */ if(kakumei_inviteexists(ka,i)!=0) { /* retry login */ wk_serve_buffer_as_file(web,connid,"/",1,"text/plain"); log_write("NEWU","invalid invite %s, redirecting to login",i); return(wkact_finished); } /* create user */ if(pass_new(ka,u,p)!=0) { /* error with username */ wk_serve_error(web,connid,wkerr_internal); log_write("NEWU","invalid user, send error"); return(wkact_finished); } /* delete invitation */ kakumei_invitedel(ka,i); /* create session and go to "posts" page */ if(session_new(ka,u,session,sizeof(session))==NULL) { /* "autologin" didn't work, ask for login */ wk_serve_buffer_as_file(web,connid,"/",1,"text/plain"); log_write("NEWU","couldn't generate new session, redirecting to login"); return(wkact_finished); } /* valid login */ snprintf(reply,sizeof(reply),"/posts.html?s=%s",session); reply[sizeof(reply)-1]='\0'; wk_serve_buffer_as_file(web,connid,reply,strlen(reply),"text/plain"); log_write("NEWU","Reply: %s",reply); return(wkact_finished); }