/*
 * kakumei_session.c
 *
 * Session handling for kakumei.
 *
 * Author: Dario Rodriguez dario@softhome.net
 * This progran is licensed under the terms of the Affero GPL v1+
 */

#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <fcntl.h>
#include <time.h>
#include <mhash.h>
#include "kakumei.h"
#include "kakumei_session.h"

char *
session_new(kakumei *ka, char *user, char *session, int sessionsize)
{
        static int init=0;
        MHASH td;
        struct timeval tv;
        struct timezone tz;
        int i;
        long n;
        char c;
        char binhash[32];
        char filename[1024];
        int len;
        int fd;
        char oldsession[SESSIONSIZE];
        if(ka==NULL || user==NULL || session==NULL || sessionsize<SESSIONSIZE || kakumei_uservalid(ka,user)!=0)
                return(NULL);
        if(init==0) {
                gettimeofday(&tv,&tz);
                srandom(tv.tv_sec+getpid()+tv.tv_usec);
                init=1;
        }
        /* generate a not-entirely-trivial-to-guess hash */
        if((td=mhash_init(MHASH_SHA256))==MHASH_FAILED)
                return(NULL);
        gettimeofday(&tv,&tz);
        mhash(td,&tv,sizeof(tv));
        mhash(td,user,strlen(user));
        for(i=0;i<20;i++) {
                n=random();
                mhash(td,&n,sizeof(n));
        }
        mhash_deinit(td,&binhash);
        for(i=0;i<sizeof(binhash);i++) {
                c=(((unsigned char *)binhash)[i]>>4);
                c=(c>=10)?(c-10+'a'):c+'0';
                session[i<<1]=c;
                c=(((unsigned char *)binhash)[i]&0xf);
                c=(c>=10)?(c-10+'a'):c+'0';
                session[(i<<1)+1]=c;
        }
        session[sizeof(binhash)]='\0';
        /* save the hash */
        mkdir(DATADIR,0700);
        mkdir(SESSIONSDIR,0700);
        snprintf(filename,sizeof(filename)-1,"%s/%s",SESSIONSDIR,session);
        filename[sizeof(filename)-1]='\0';
        if((fd=open(filename,O_WRONLY|O_TRUNC|O_CREAT,0600))==-1)
                return(NULL);
        len=strlen(user);
        if(write(fd,user,len)!=len) {
                close(fd),fd=-1;
                return(NULL);
        }
        close(fd),fd=-1;
        /* delete the previous session of the user */
        mkdir(DATADIR,0700);
        mkdir(USERSDIR,0700);
        snprintf(filename,sizeof(filename)-1,"%s/%s/session",USERSDIR,user);
        filename[sizeof(filename)-1]='\0';
        if((fd=open(filename,O_RDONLY))!=-1) {
                memset(oldsession,0,sizeof(oldsession));
                read(fd,oldsession,sizeof(oldsession)-1);
                close(fd),fd=-1;
                session_del(ka,oldsession);
        }
        /* write the current session */
        if((fd=open(filename,O_WRONLY|O_TRUNC|O_CREAT,0600))!=-1) {
                write(fd,session,strlen(session));
                close(fd),fd=-1;
        }
        /* success */
        return(session);
}

char *
session_check(kakumei *ka, char *session, char *user, int usersize)
{
        int i;
        int fd;
        char filename[1024];
        if(ka==NULL || session==NULL || session[0]=='\0' || user==NULL || usersize<(MAXUSERSIZE+1))
                return(NULL);
        for(i=0;session[i]!='\0';i++) {
                if(!(session[i]>='0' && session[i]<='0') &&
                   !(session[i]>='a' && session[i]<='f')) {
                        return(NULL);
                }
        }
        snprintf(filename,sizeof(filename)-1,"%s/%s",SESSIONSDIR,session);
        filename[sizeof(filename)-1]='\0';
        if((fd=open(filename,O_RDONLY))==-1)
                return(NULL);
        memset(user,0,sizeof(usersize));
        read(fd,user,usersize-1);
        close(fd),fd=-1;
        if(kakumei_uservalid(ka,user)!=0)
                return(NULL);
        return(user);
}

int
session_del(kakumei *ka, char *session)
{
        int i;
        char filename[1024];
        if(ka==NULL || session==NULL || session[0]=='\0')
                return(-1);
        for(i=0;session[i]!='\0';i++) {
                if(!(session[i]>='0' && session[i]<='0') &&
                   !(session[i]>='a' && session[i]<='f')) {
                        return(-1);
                }
        }
        snprintf(filename,sizeof(filename)-1,"%s/%s",SESSIONSDIR,session);
        filename[sizeof(filename)-1]='\0';
        unlink(filename);
        return(0);
}