#include "bbs.h"

int iniconnecttouserd(const PID_T inipid, const PID_T remotepid,
                      char *sockname, confrecordtyp *confrecord)
{
  int n, sockd;
  char bstr[BEFSTRLEN+1];
    
  if ((sockd=connect2bbsd(confrecord))<0) {
    errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,"bbs","iniconnecttouserd",
	      "connection to daemon failed");
    exit(1);
  }
  sprintf(bstr,"#%2d\n",DO_TALKINIT);
  sendn(sockd,(void *)bstr,(SIZE_T)BEFSTRLEN,0);
  sendn(sockd,(void *)&inipid,(SIZE_T)sizeof(PID_T),0);
  sendn(sockd,(void *)&remotepid,(SIZE_T)sizeof(PID_T),0);
  recvn(sockd,(void *)&n,(SIZE_T)sizeof(int),0);
  if (n == 0) {
    recvn(sockd,(void *)sockname,(SIZE_T)TALKSOCKNAMELEN,0);
    recvn(sockd,(void *)&n,(SIZE_T)sizeof(int),0);
  }
  unconnect2bbsd(sockd,confrecord);
  return(n);
}


int remconnecttouserd(sessionrecordtyp *sr, char *sockname, const PID_T mypid,
                      confrecordtyp *confrecord)
{
  int n, sockd;
  char bstr[BEFSTRLEN+1];

  if ((sockd=connect2bbsd(confrecord))<0) {
    errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,"bbs","remconnecttouserd",
	      "connection to daemon failed");
    exit(1);
  }
  sprintf(bstr,"#%2d\n",DO_TALKREMOTE);
  sendn(sockd,(void *)bstr,(SIZE_T)BEFSTRLEN,0);
  sendn(sockd,(void *)&mypid,(SIZE_T)sizeof(PID_T),0);
  recvn(sockd,(void *)&n,(SIZE_T)sizeof(int),0);
  if (n == 0) {
    recvn(sockd,(void *)sockname,(SIZE_T)TALKSOCKNAMELEN,0);
    recvn(sockd,(void *)sr,(SIZE_T)sizeof(sessionrecordtyp),0);
    recvn(sockd,(void *)&n,(SIZE_T)sizeof(int),0);
  }
  unconnect2bbsd(sockd,confrecord);
  return(n);
}


int bbslog(const int level, confrecordtyp *confrecord, const char *fmt, ...)
  /*
  Stellt einen String zusammen vom Format:
  PID <pid>, <datum>: msg\n
  und sendet ihn an den Daemon zum Eintragen in das Logfile,
  dabei kann fuer 'fmt' ein Format wie fuer 'printf' eingesetzt werden,
  zusaetzlich wird '%m' durch den aktuellen errno-String ersetzt.
  Ist level==LOG_CONSOLE, so wird der Text zusaetzlich auf DEV_CONSOLE
  ausgegeben,
  ist level==LOG_NEVER, so wird nichts ausgegeben
  
  errno wird erhalten
  
  bbslog darf nicht vom Daemon (bbsd) aufgerufen werden
  */
{
  va_list ap;
  SIZE_T msglen;
  int saved_errno, n=0, logs, logc;
  char c, *cp1, *cp2, str[PATH_MAX+1], fmt_cpy[PATH_MAX+1];

  if (level==LOG_NEVER)  return 0;

  saved_errno = errno;
  
  if (bbsdaemon) {
    bgerror("bbslog","bbslog cannot be used from daemon");
    return(-2);
  }
  if ((logs=connect2bbsd(confrecord))<0) {
    errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,"bbs","bbslog",
             "connection to daemon failed");
    exit(1);
  }
  sprintf(str,"#%2d\n",DO_LOG);
  sendn(logs,(void *)str,(SIZE_T)BEFSTRLEN,0);
  
  sprintf(str,"%.15s[%ld]: ",gettime()+4*sizeof(char),(long)getpid());

  va_start(ap, fmt);
  for (cp1=fmt_cpy; (c=*fmt)!='\0'; ++fmt) {
    if (c=='%' && fmt[1]=='m') {
      ++fmt;
      for (cp2=strerror(saved_errno); (*cp1=*cp2++)!='\0'; ++cp1);
    }
    else {
      *cp1++ = c;
    }
  }
  *cp1 = '\0';
  
  for (cp1=str; *cp1!='\0'; ++cp1);
  vsprintf(cp1,fmt_cpy,ap);
  va_end(ap);
  msglen = strlen(str) - 1;
  if (str[msglen++]!='\n') {
    str[msglen++] = '\n';
    str[msglen] = '\0';
  }
  sendn(logs,(void *)&msglen,(SIZE_T)sizeof(SIZE_T),0);
  sendn(logs,(void *)str,msglen,0);
  recvn(logs,(void *)&n,(SIZE_T)sizeof(int),0);
  unconnect2bbsd(logs,confrecord);
  if (n<0) {
    errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,"bbs","bbslog",
             "command bbslog wrongly executed by bbsd");
    n = -1;
  }
  
  if (level==LOG_CONSOLE) {
    if ((logc=open(DEV_CONSOLE,O_WRONLY))<0) {
      errormsg(E_SYSLOG|E_USER,confrecord,"bbs","bbslog","open %s: %m",
               DEV_CONSOLE);
      n += -2;
    }
    else {
      writen(logc,(void *)str,msglen);
      close(logc);
    }
  }
  
  errno = saved_errno;
  return n;
}


int sendstatustodaemon(const int status, confrecordtyp *confrecord)
{
  int sockd, n;
  PID_T pid;
  char bstr[BEFSTRLEN+1];
  
  if ((sockd=connect2bbsd(confrecord))<0) {
    errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,"bbs","sendstatustodaemon",
             "connection to daemon failed");
    exit(1);
  }
  sprintf(bstr,"#%2d\n",DO_SENDSTATUS);
  sendn(sockd,(void *)bstr,(SIZE_T)BEFSTRLEN,0);
  pid = getpid();
  sendn(sockd,(void *)&pid,(SIZE_T)sizeof(PID_T),0);
  sendn(sockd,(void *)&status,(SIZE_T)sizeof(int),0);
  recvn(sockd,(void *)&n,(SIZE_T)sizeof(int),0);
  unconnect2bbsd(sockd,confrecord);
  if (n<0) {
    errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,"bbs","sendstatustodaemon",
             "command endstatustodaemon wrongly executed by bbsd");
    return -1;
  }
  return n;
}


int getsessions(sessionrecordtyp sessionrecords[], const int maxn,
                confrecordtyp *confrecord)
  /*
  Erfragt vom Daemon alle sessionrecords der laufenden bbs und gibt sie
  in sessionrecords zurueck, der Rueckgabewert von getsessions ist die
  Anzahl der sessions.
  maxn begrenzt die maximale Anzahl der sessions
  */
{
  int sockd, n=-1;
  char bstr[BEFSTRLEN+1];
  sessionrecordtyp sr;
  
  if ((sockd=connect2bbsd(confrecord))<0) {
    errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,"bbs","getsessions",
             "connection to daemon failed");
    exit(1);
  }
  sprintf(bstr,"#%2d\n",DO_GETSESSIONS);
  sendn(sockd,(void *)bstr,(SIZE_T)BEFSTRLEN,0);
  if (maxn>0) {
    do {
      n++;
      recvn(sockd,(void *)&(sessionrecords[n]),(SIZE_T)sizeof(sessionrecordtyp),0);
    } while (sessionrecords[n].pid!=0 && n<maxn-1);
    sr.pid = sessionrecords[n].pid;
  }
  else {
    sr.pid = 1;
  }
  while (sr.pid!=0) {
    recvn(sockd,(void *)&sr,(SIZE_T)sizeof(sessionrecordtyp),0);
  }
  recvn(sockd,(void *)&n,(SIZE_T)sizeof(int),0);
  unconnect2bbsd(sockd,confrecord);
  if (n<0) {
    errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,"bbs","getsessions",
             "command getsessions wrongly executed by bbsd");
    return -1;
  }
  return n;
}


int addsession(const sessionrecordtyp *sessionrecord,
               confrecordtyp *confrecord)
  /*
  Sendet sessionrecord zum Eintragen zum Daemon
  */
{
  int n, sockd;
  char bstr[BEFSTRLEN+1];
  
  if ((sockd=connect2bbsd(confrecord))<0) {
    errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,"bbs","addsession",
             "connection to daemon failed");
    exit(1);
  }
  sprintf(bstr,"#%2d\n",DO_ADDSESSION);
  sendn(sockd,(void *)bstr,(SIZE_T)BEFSTRLEN,0);
  sendn(sockd,(void *)sessionrecord,(SIZE_T)sizeof(sessionrecordtyp),0);
  recvn(sockd,(void *)&n,(SIZE_T)sizeof(int),0);
  unconnect2bbsd(sockd,confrecord);
  if (n<0) {
    errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,"bbs","addsession",
             "command addsession wrongly executed by bbsd");
    return -1;
  }
  return 0;
}


int removesession(const PID_T pid, confrecordtyp *confrecord)
  /*
  Sendet pid zum Loeschen der zugehoerigen session zum Daemon
  */
{
  int n, sockd;
  char bstr[BEFSTRLEN+1];
  
  if ((sockd=connect2bbsd(confrecord))<0) {
    errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,"bbs","removebbspid",
             "connection to daemon failed");
    exit(1);
  }
  sprintf(bstr,"#%2d\n",DO_REMOVESESSION);
  sendn(sockd,(void *)bstr,(SIZE_T)BEFSTRLEN,0);
  sendn(sockd,(void *)&pid,(SIZE_T)sizeof(PID_T),0);
  recvn(sockd,(void *)&n,(SIZE_T)sizeof(int),0);
  unconnect2bbsd(sockd,confrecord);
  if (n<0) {
    errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,"bbs","removebbspid",
             "command addbbspid wrongly executed by bbsd");
    return -1;
  }
  return 0;
}


boolean getuserrecord(userrecordtyp *userrecord, char *username,
                      confrecordtyp *confrecord)
  /*
  Erfragt den Userrecord des Users username vom Daemon
  Der Rueckgabewert von getuserrecord ist FALSE, wenn der User nicht existiert
  */
{
  int n, sockd;
  char bstr[BEFSTRLEN+1];
  
  if ((sockd=connect2bbsd(confrecord))<0) {
    errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,"bbs","getuserrecord",
             "connection to daemon failed");
    exit(1);
  }
  sprintf(bstr,"#%2d\n",DO_GETUSERRECORD);
  sendn(sockd,(void *)bstr,(SIZE_T)BEFSTRLEN,0);
  sendn(sockd,(void *)username,(SIZE_T)S_STRLEN,0);
  n = (int)recvn(sockd,(void *)userrecord,(SIZE_T)sizeof(userrecordtyp),0);
  recvn(sockd,(void *)&n,(SIZE_T)sizeof(int),0);
  unconnect2bbsd(sockd,confrecord);
  if (n<0) {
    errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,"bbs","getuserrecord",
             "command addbbspid wrongly executed by bbsd");
    return FALSE;
  }
  return (n==0);
}


int saveuserrecord(userrecordtyp *userrecord, confrecordtyp *confrecord)
  /*
  Sendet den Userrecord userrecord zum Daemon, der ihn dann im Userfile
  permanent speichern soll
  */
{
  int n, sockd;
  char bstr[BEFSTRLEN+1];
  
  if ((sockd=connect2bbsd(confrecord))<0) {
    errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,"bbs","saveuserrecord",
             "connection to daemon failed");
    exit(1);
  }
  sprintf(bstr,"#%2d\n",DO_SAVEUSERRECORD);
  sendn(sockd,(void *)bstr,(SIZE_T)BEFSTRLEN,0);
  sendn(sockd,(void *)userrecord,(SIZE_T)sizeof(userrecordtyp),0);
  recvn(sockd,(void *)&n,(SIZE_T)sizeof(int),0);
  unconnect2bbsd(sockd,confrecord);
  if (n<0) {
    errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,"bbs","saveuserrecord",
             "command addbbspid wrongly executed by bbsd");
    return FALSE;
  }
  return (n==0);
}


int connect2bbsd(confrecordtyp *confrecord)
  /*
  Stellt eine Verbindung zum Daemon (bbsd) her, der Deskriptor wird als
  Rueckgabewert geliefert.
  Die meisten Signale werden blockiert, wird in unconnect2bbsd restauriert
  */
{
  int sockd, len, k, n;
  struct sockaddr_un saun;
  SIGSET_T sigset;
  
  if (blocksigsetstatus == SIGMASK_UNSET) {
    sigfillset(&sigset);
    sigprocmask(SIG_SETMASK,&sigset,&old_blocksigset);
    blocksigsetstatus = SIGMASK_SET;
  }
  else {
    errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,"bbs","connect2bbsd",
             "WARNING: old_blocksigset is in use");
  }
  if ((sockd=socket(AF_UNIX,SOCK_STREAM,0))<0) {
    errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,"bbs","connect2bbsd",
             "socket %s: %m",confrecord->sockpath);
    return -1;
  }
  BZERO(&saun, sizeof(saun));
  saun.sun_family = AF_UNIX;
  strcpy(saun.sun_path,confrecord->sockpath);
  len = SUN_LEN(&saun);
#ifdef SCM_RIGHTS
  saun.sun_len = len;
#endif
  k = 0;
  while ((n=connect(sockd,(struct sockaddr *)&saun,len))<0 && k<SOCKCONTRIES) {
    k++;
    sleep(1);
  }
  if (n<0) {
    errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,"bbs","connect2bbsd",
             "connect %s: %m",confrecord->sockpath);
    return n;
  }
  n = strlen(confrecord->authkey) + 1;
  sendn(sockd,(void *)&n,(SIZE_T)sizeof(int),0);
  sendn(sockd,(void *)confrecord->authkey,(SIZE_T)n,0);
  k = recvn(sockd,(void *)&n,(SIZE_T)sizeof(int),0);
  if (k<0 || n<0) {
    errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,"bbs","connect2bbsd",
             "wrong authorisation");
    return n;
  }
  return sockd;
}


int unconnect2bbsd(const int sockd, confrecordtyp *confrecord)
{
  if (blocksigsetstatus == SIGMASK_SET) {
    blocksigsetstatus = SIGMASK_UNSET;
    sigprocmask(SIG_SETMASK,&old_blocksigset,NULL);
  }
  else {
    errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,"bbs","unconnect2bbsd",
             "WARNING: old_blocksigset is not set (nothing done)");
  }
  return close(sockd);
}
