/*
 * BBS implementation dependendent part
 *
 * The only two interfaces you must provide
 *
 * #include "inntobbs.h" int receive_article(); 0 success not 0 fail
 *
 * if (storeDB(HEADER[MID_H], hispaths) < 0) { .... fail }
 *
 * int cancel_article_front( char *msgid );          0 success not 0 fail
 *
 * char *ptr = (char*)DBfetch(msgid);
 *
 * ���줧�峹���e (body)�b char *BODY, ���Y (header)�b char *HEADER[] SUBJECT_H,
 * FROM_H, DATE_H, MID_H, NEWSGROUPS_H, NNTPPOSTINGHOST_H, NNTPHOST_H,
 * CONTROL_H, PATH_H, ORGANIZATION_H
 */

/*
 * Sample Implementation
 *
 * receive_article()         --> post_article()   --> bbspost_write_post();
 * cacnel_article_front(mid) --> cancel_article() --> bbspost_write_cancel();
 */


#ifndef PowerBBS
#include "innbbsconf.h"
#include "daemon.h"
#include "bbslib.h"
#include "inntobbs.h"
#include "antisplam.h"

extern int Junkhistory;

char *post_article ARG((char *, char *, char *, int (*) (), char *, char *));
int cancel_article ARG((char *, char *, char *));


#ifdef  MapleBBS
#include "config.h"
#include "pttstruct.h"
#define _BBS_UTIL_C_
#else
report()
{
  /* Function called from record.o */
  /* Please leave this function empty */
}
#endif


#if defined(PalmBBS)

#ifndef PATH
# define PATH XPATH
#endif

#ifndef HEADER
# define HEADER XHEADER
#endif

#endif

/* process post write */
bbspost_write_post(fh, board, filename)
  int fh;
  char *board;
  char *filename;
{
  char *fptr, *ptr;
  FILE *fhfd = fdopen(fh, "w");

  if (fhfd == NULL)
  {
    bbslog("can't fdopen, maybe disk full\n");
    return -1;
  }

  fprintf(fhfd, "�o�H�H: %.60s, �ݪO: %s\n", FROM, board);
  fprintf(fhfd, "��  �D: %.70s\n", SUBJECT);
  fprintf(fhfd, "�o�H��: %.43s (%s)\n", SITE, DATE);
  fprintf(fhfd, "��H��: %.70s\n", PATH);

#ifndef MapleBBS
  if (POSTHOST != NULL)
  {
    fprintf(fhfd, "Origin: %.70s\n", POSTHOST);
  }
#endif

  fprintf(fhfd, "\n");
  for (fptr = BODY, ptr = strchr(fptr, '\r'); ptr != NULL && *ptr != '\0'; fptr = ptr + 1, ptr = strchr(fptr, '\r'))
  {
    int ch = *ptr;
    *ptr = '\0';
    fputs(fptr, fhfd);
    *ptr = ch;
  }
  fputs(fptr, fhfd);

  fflush(fhfd);
  fclose(fhfd);
  return 0;
}

#ifdef  KEEP_NETWORK_CANCEL
/* process cancel write */
bbspost_write_cancel(fh, board, filename)
  int fh;
  char *board, *filename;
{
  char *fptr, *ptr;
  FILE *fhfd = fdopen(fh, "w"), *fp;
  char buffer[256];

  if (fhfd == NULL)
  {
    bbslog("can't fdopen, maybe disk full\n");
    return -1;
  }

  fprintf(fhfd, "�o�H�H: %s, �H��: %s\n", FROM, board);
  fprintf(fhfd, "��  �D: %s\n", SUBJECT);
  fprintf(fhfd, "�o�H��: %.43s (%s)\n", SITE, DATE);
  fprintf(fhfd, "��H��: %.70s\n", PATH);
  if (HEADER[CONTROL_H] != NULL)
  {
    fprintf(fhfd, "Control: %s\n", HEADER[CONTROL_H]);
  }
  if (POSTHOST != NULL)
  {
    fprintf(fhfd, "Origin: %s\n", POSTHOST);
  }
  fprintf(fhfd, "\n");
  for (fptr = BODY, ptr = strchr(fptr, '\r'); ptr != NULL && *ptr != '\0'; fptr = ptr + 1, ptr = strchr(fptr, '\r'))
  {
    int ch = *ptr;
    *ptr = '\0';
    fputs(fptr, fhfd);
    *ptr = ch;
  }
  fputs(fptr, fhfd);
  if (POSTHOST != NULL)
  {
    fprintf(fhfd, "\n * Origin: �� %.26s �� From: %.40s\n", SITE, POSTHOST);
  }
  fprintf(fhfd, "\n---------------------\n");
  fp = fopen(filename, "r");
  if (fp == NULL)
  {
    bbslog("can't open %s\n", filename);
    return -1;
  }
  while (fgets(buffer, sizeof buffer, fp) != NULL)
  {
    fputs(buffer, fhfd);
  }
  fclose(fp);
  fflush(fhfd);
  fclose(fhfd);

  {
    fp = fopen(filename, "w");
    if (fp == NULL)
    {
      bbslog("can't write %s\n", filename);
      return -1;
    }
    fprintf(fp, "�o�H�H: %s, �H��: %s\n", FROM, board);
    fprintf(fp, "��  �D: %.70s\n", SUBJECT);
    fprintf(fp, "�o�H��: %.43s (%s)\n", SITE, DATE);
    fprintf(fp, "��H��: %.70s\n", PATH);
    if (POSTHOST != NULL)
    {
      fprintf(fhfd, "Origin: %s\n", POSTHOST);
    }
    if (HEADER[CONTROL_H] != NULL)
    {
      fprintf(fhfd, "Control: %s\n", HEADER[CONTROL_H]);
    }
    fprintf(fp, "\n");
    for (fptr = BODY, ptr = strchr(fptr, '\r'); ptr != NULL && *ptr != '\0'; fptr = ptr + 1, ptr = strchr(fptr, '\r'))
    {
      *ptr = '\0';
      fputs(fptr, fp);
    }
    fputs(fptr, fp);
    if (POSTHOST != NULL)
    {
      fprintf(fp, "\n * Origin: �� %.26s �� From: %.40s\n", SITE, POSTHOST);
    }
    fclose(fp);
  }
  return 0;
}
#endif


bbspost_write_control(fh, board, filename)
  int fh;
  char *board;
  char *filename;
{
  char *fptr, *ptr;
  FILE *fhfd = fdopen(fh, "w");

  if (fhfd == NULL)
  {
    bbslog("can't fdopen, maybe disk full\n");
    return -1;
  }

  fprintf(fhfd, "Path: %s!%s\n", MYBBSID, HEADER[PATH_H]);
  fprintf(fhfd, "From: %s\n", FROM);
  fprintf(fhfd, "Newsgroups: %s\n", GROUPS);
  fprintf(fhfd, "Subject: %s\n", SUBJECT);
  fprintf(fhfd, "Date: %s\n", DATE);
  fprintf(fhfd, "Organization: %s\n", SITE);
  if (POSTHOST != NULL)
  {
    fprintf(fhfd, "NNTP-Posting-Host: %.70s\n", POSTHOST);
  }
  if (HEADER[CONTROL_H] != NULL)
  {
    fprintf(fhfd, "Control: %s\n", HEADER[CONTROL_H]);
  }
  if (HEADER[APPROVED_H] != NULL)
  {
    fprintf(fhfd, "Approved: %s\n", HEADER[APPROVED_H]);
  }
  if (HEADER[DISTRIBUTION_H] != NULL)
  {
    fprintf(fhfd, "Distribution: %s\n", HEADER[DISTRIBUTION_H]);
  }
  fprintf(fhfd, "\n");
  for (fptr = BODY, ptr = strchr(fptr, '\r'); ptr != NULL && *ptr != '\0'; fptr = ptr + 1, ptr = strchr(fptr, '\r'))
  {
    int ch = *ptr;
    *ptr = '\0';
    fputs(fptr, fhfd);
    *ptr = ch;
  }
  fputs(fptr, fhfd);


  fflush(fhfd);
  fclose(fhfd);
  return 0;
}


time_t datevalue;


/* process cancel write */
receive_article()
{
  int i;
  char *user, *userptr;
  char *ngptr, *nngptr, *pathptr;
  char **splitptr;
  static char userid[32];
  static char xdate[32];
  static char xpath[180];
  newsfeeds_t *nf;
  char *boardhome;
  char hispaths[4096];
  char firstpath[MAXPATHLEN], *firstpathbase;
  char *lesssym, *nameptrleft, *nameptrright;
  static char sitebuf[80];

#ifdef HMM_USE_ANTI_SPAM
 char *notitle[] =
    {"��P", "�~�ȥN��", "�x", "��T", "�K�O", "�j�ذe", "�ǾP", "����",
     "�~�O", "�ǩI", "��", "�ާA��", "�����~", "���ɧJ", "�H��", "�ȿ�",
     "=?", "!!!",
     "�ާA", "�ާA", "�F�p", "�ީp","**","����","����","�C�C","�w��", NULL},
    *nofrom[] =
    {"TaipeiNet.Net", "hotmail.com", "mt.touc.edu.tw", "ms11.hinet.net", NULL}, 
    *nocont[] =
    {"�W�B����", "�u�f��", "�ʧ@�n��", "�q��", "�S��", "�M��", "BBC",
     "�F�A", "�ާA", "�F�p", "�ީp","�C��","�̷s�ؿ�", "http://", "����",
     "�з~", "�I��", "�s�i�H", "�u��", "����", "NCg", "ICAg", NULL};  
#endif

  if (FROM == NULL)
  {
    bbslog(":Err: article without usrid %s\n", MSGID);
    return 0;
  }
  else 
  {
#ifdef HMM_USE_ANTI_SPAM
    for(i=0; nofrom[i]; i++)
      if(strstr(FROM, nofrom[i]))
       {
         morelog_to(INNBBSD_SPAM, "spam from [%s]: %s\n", nofrom[i], FROM);
         morelog_to(INNBBSD_SPAM, "  %s %s %s %s\n", FROM, PATH, GROUPS, SUBJECT);
         bbslog(":Ptt: spam from [%s]: %s\n", nofrom[i], FROM);
         return 0;
       }
#endif
  }

  if(!BODY)
  {
    bbslog(":Err: article without body %s\n", MSGID);
    return 0;
  }
  else
  {
#ifdef HMM_USE_ANTI_SPAM
    for(i=0; nocont[i]; i++)
      if(strstr(BODY, nocont[i]))
       {
         morelog_to(INNBBSD_SPAM, "spam body [%s]: %s\n", nocont[i]);
         morelog_to(INNBBSD_SPAM, "  %s %s %s %s\n", FROM, PATH, GROUPS, SUBJECT);
         bbslog(":Ptt: spam body [%s]: %s\n", nocont[i]);
         return 0;
       }   
#endif
  }

  if(!SUBJECT)
  {
    bbslog(":Err: article without subject %s\n", MSGID);
    return 0;
  }
  else
  {
#ifdef HMM_USE_ANTI_SPAM
    for(i=0; notitle[i]; i++)
      if(strstr(SUBJECT, notitle[i]))
       {
         morelog_to(INNBBSD_SPAM, "spam title [%s]: %s\n", notitle[i], SUBJECT);
         morelog_to(INNBBSD_SPAM, "  %s %s %s %s\n", FROM, PATH, GROUPS, SUBJECT);
         bbslog(":Ptt: spam title [%s]: %s\n", notitle[i], SUBJECT);
         return 0;
       }
#endif
  }


  user = (char *) strchr(FROM, '@');
  lesssym = (char *) strchr(FROM, '<');
  nameptrleft = NULL, nameptrright = NULL;
  if (lesssym == NULL || lesssym >= user)
  {
    lesssym = FROM;
    nameptrleft = strchr(FROM, '(');
    if (nameptrleft != NULL)
      nameptrleft++;
    nameptrright = strrchr(FROM, ')');
  }
  else
  {
    nameptrleft = FROM;
    nameptrright = strrchr(FROM, '<');
    lesssym++;
  }
  if (user != NULL)
  {
    *user = '\0';
    userptr = (char *) strchr(FROM, '.');
    if (userptr != NULL)
    {
      *userptr = '\0';
      strncpy(userid, lesssym, sizeof userid);
      *userptr = '.';
    }
    else
    {
      strncpy(userid, lesssym, sizeof userid);
    }
    *user = '@';
  }
  else
  {
    strncpy(userid, lesssym, sizeof userid);
  }
  strcat(userid, ".");

  {
      struct tm tmbuf;
      
      strptime(DATE, "%d %b %Y %X GMT", &tmbuf);
      datevalue = timegm(&tmbuf);
  }

  if (datevalue > 0)
  {
    char *p;
    strncpy(xdate, ctime(&datevalue), sizeof(xdate));
    p = (char *) strchr(xdate, '\n');
    if (p != NULL)
      *p = '\0';
    DATE = xdate;
  }

#ifndef MapleBBS
  if (SITE == NULL || *SITE == '\0')
  {
    if (nameptrleft != NULL && nameptrright != NULL)
    {
      char savech = *nameptrright;
      *nameptrright = '\0';
      strncpy(sitebuf, nameptrleft, sizeof sitebuf);
      *nameptrright = savech;
      SITE = sitebuf;
    }
    else
      /* SITE = "(Unknown)"; */
      SITE = "";
  }
  if (strlen(MYBBSID) > 70)
  {
    bbslog(" :Err: your bbsid %s too long\n", MYBBSID);
    return 0;
  }
#endif

  sprintf(xpath, "%s!%.*s", MYBBSID, sizeof(xpath) - strlen(MYBBSID) - 2, PATH);
  PATH = xpath;
  for (pathptr = PATH; pathptr != NULL && (pathptr = strstr(pathptr, ".edu.tw")) != NULL;)
  {
    if (pathptr != NULL)
    {
      strcpy(pathptr, pathptr + 7);
    }
  }
  xpath[71] = '\0';

#ifndef MapleBBS
  echomaillog();
#endif

  *hispaths = '\0';
  splitptr = (char **) BNGsplit(GROUPS);
  firstpath[0] = '\0';
  firstpathbase = firstpath;

  for (ngptr = *splitptr; ngptr != NULL; ngptr = *(++splitptr))
  {
    char *boardptr, *nboardptr;

    if (*ngptr == '\0')
      continue;
    nf = (newsfeeds_t *) search_group(ngptr);
    if (nf == NULL)
    {
      bbslog("unwanted \'%s\'\n", ngptr);
      continue;
    }
    if (nf->board == NULL || !*nf->board)
      continue;
    if (nf->path == NULL || !*nf->path)
      continue;
    for (boardptr = nf->board, nboardptr = (char *) strchr(boardptr, ','); boardptr != NULL && *boardptr != '\0'; nboardptr = (char *) strchr(boardptr, ','))
    {
      if (nboardptr != NULL)
      {
        *nboardptr = '\0';
      }
      if (*boardptr == '\t')
      {
        goto boardcont;
      }
      boardhome = (char *) fileglue("%s/boards/%c/%s", BBSHOME, boardptr[0], boardptr);
      if (!isdir(boardhome))
      {
        bbslog(":Err: unable to write %s\n", boardhome);
      }
      else
      {
        char *fname;
        /*
         * if ( !isdir( boardhome )) { bbslog( ":Err: unable to write
         * %s\n",boardhome); testandmkdir(boardhome); }
         */
        fname = (char *) post_article(boardhome, userid, boardptr, 
                                 bbspost_write_post, NULL, firstpath);
        if (fname != NULL)
        {
          fname = (char *) fileglue("%s/%s", boardptr, fname);
          if (firstpath[0] == '\0')
          {
	    sprintf(firstpath, "%s/boards/%c, %s", BBSHOME, fname[0], fname);
            firstpathbase = firstpath + strlen(BBSHOME) + strlen("/boards/x/");
          }
          if (strlen(fname) + strlen(hispaths) + 1 < sizeof(hispaths))
          {
            strcat(hispaths, fname);
            strcat(hispaths, " ");
          }
        }
        else
        {
          bbslog("fname is null %s\n", boardhome);
          return -1;
        }
      }

  boardcont:
      if (nboardptr != NULL)
      {
        *nboardptr = ',';
        boardptr = nboardptr + 1;
      }
      else
        break;

    }                           /* for board1,board2,... */
    /*
     * if (nngptr != NULL) ngptr = nngptr + 1; else break;
     */
    if (*firstpathbase)
      feedfplog(nf, firstpathbase, 'P');
  }
  if (*hispaths)
    bbsfeedslog(hispaths, 'P');

  if (Junkhistory || *hispaths)
  {
    if (storeDB(HEADER[MID_H], hispaths) < 0)
    {
      bbslog("store DB fail\n");
      /* I suspect here will introduce duplicated articles */
      /* return -1; */
    }
  }
  return 0;
}

receive_control()
{
  char *boardhome, *fname;
  char firstpath[MAXPATHLEN], *firstpathbase;
  char **splitptr, *ngptr;
  newsfeeds_t *nf;

  bbslog("control post %s\n", HEADER[CONTROL_H]);
  boardhome = (char *) fileglue("%s/boards/c/control", BBSHOME);
  testandmkdir(boardhome);
  *firstpath = '\0';
  if (isdir(boardhome))
  {
    fname = (char *) post_article(boardhome, FROM, "control", bbspost_write_control, NULL, firstpath);
    if (fname != NULL)
    {
      if (firstpath[0] == '\0')
	sprintf(firstpath, "%s/boards/c/control/%s", BBSHOME, fname);
      if (storeDB(HEADER[MID_H], (char *) fileglue("control/%s", fname)) < 0)
      {
      }
      bbsfeedslog(fileglue("control/%s", fname), 'C');
      firstpathbase = firstpath + strlen(BBSHOME) + strlen("/boards/x/");
      splitptr = (char **) BNGsplit(GROUPS);
      for (ngptr = *splitptr; ngptr != NULL; ngptr = *(++splitptr))
      {
        if (*ngptr == '\0')
          continue;
        nf = (newsfeeds_t *) search_group(ngptr);
        if (nf == NULL)
          continue;
        if (nf->board == NULL)
          continue;
        if (nf->path == NULL)
          continue;
        feedfplog(nf, firstpathbase, 'C');
      }
    }
  }
  return 0;
}

cancel_article_front(msgid)
  char *msgid;
{
  char *ptr = (char *) DBfetch(msgid);
  char *filelist, filename[2048];
  char histent[4096];
  char firstpath[MAXPATHLEN], *firstpathbase;
  if (ptr == NULL)
  {
    bbslog("cancel failed(DBfetch): %s\n", msgid);
    return 0;
  }
  strncpy(histent, ptr, sizeof histent);
  ptr = histent;

#ifdef DEBUG
  printf("**** try to cancel %s *****\n", ptr);
#endif

  filelist = strchr(ptr, '\t');
  if (filelist != NULL)
  {
    filelist++;
  }
  *firstpath = '\0';
  for (ptr = filelist; ptr && *ptr;)
  {
    char *file;
    for (; *ptr && isspace(*ptr); ptr++);
    if (*ptr == '\0')
      break;
    file = ptr;
    for (ptr++; *ptr && !isspace(*ptr); ptr++);
    if (*ptr != '\0')
    {
      *ptr++ = '\0';
    }
    sprintf(filename, "%s/boards/%c/%s", BBSHOME, file[0], file);
    bbslog("cancel post %s\n", filename);
    if (isfile(filename))
    {
      FILE *fp = fopen(filename, "r");
      char buffer[1024];
      char xfrom0[100], xfrom[100], xpath[1024], *boardhome;

      if (fp == NULL)
        continue;
      strncpy(xfrom0, HEADER[FROM_H], 99);
      xfrom0[99] = 0;
      strtok(xfrom0, ", ");
      while (fgets(buffer, sizeof buffer, fp) != NULL)
      {
        char *hptr;
        if (buffer[0] == '\n')
          break;
        hptr = strchr(buffer, '\n');
        if (hptr != NULL)
          *hptr = '\0';
        if (strncmp(buffer, "�o�H�H: ", 8) == 0)
        {
          strncpy(xfrom, buffer + 8, 99);
          xfrom[99] = 0;
          strtok(xfrom, ", ");
        }
        else if (strncmp(buffer, "��H��: ", 8) == 0)
        {
          strcpy(xpath, buffer + 8);
        }
      }
      fclose(fp);
      if (strcmp(xfrom0, xfrom))
      {
        bbslog("Invalid cancel %s, path: %s!%s, [`%s` != `%s`]\n",
           FROM, MYBBSID, PATH, xfrom0, xfrom);
        return 0;
      }

#ifdef  KEEP_NETWORK_CANCEL
      bbslog("cancel post %s\n", filename);
      boardhome = (char *) fileglue("%s/boards/d/deleted", BBSHOME);
      testandmkdir(boardhome);
      if (isdir(boardhome))
      {
        char subject[1024];
        char *fname;
        if (POSTHOST)
        {
          sprintf(subject, "cancel by: %.1000s", POSTHOST);
        }
        else
        {
          char *body, *body2;
          body = strchr(BODY, '\r');
          if (body != NULL)
            *body = '\0';
          body2 = strchr(BODY, '\n');
          if (body2 != NULL)
            *body = '\0';
          sprintf(subject, "%.1000s", BODY);
          if (body != NULL)
            *body = '\r';
          if (body2 != NULL)
            *body = '\n';
        }
        if (*subject)
          SUBJECT = subject;
        fname = (char *) post_article(boardhome, FROM, "deleted", bbspost_write_cancel, filename, firstpath);
        if (fname != NULL)
        {
          if (firstpath[0] == '\0')
          {
            sprintf(firstpath, "%s/boards/d/deleted/%s", BBSHOME, fname);
            firstpathbase = firstpath + strlen(BBSHOME) + strlen("/boards/x/");
          }
          if (storeDB(HEADER[MID_H], (char *) fileglue("deleted/%s", fname)) < 0)
          {
            /* should do something */
            bbslog("store DB fail\n");
            /* return -1; */
          }
          bbsfeedslog(fileglue("deleted/%s", fname), 'D');

#ifdef OLDDISPATCH
          {
            char board[256];
            newsfeeds_t *nf;
            char *filebase = filename + strlen(BBSHOME) + strlen("/boards/x/");
            char *filetail = strrchr(filename, '/');
            if (filetail != NULL)
            {
              strncpy(board, filebase, filetail - filebase);
              nf = (newsfeeds_t *) search_board(board);
              if (nf != NULL && nf->board && nf->path)
              {
                feedfplog(nf, firstpathbase, 'D');
              }
            }
          }
#endif
        }
        else
        {
          bbslog(" fname is null %s %s\n", boardhome, filename);
          return -1;
        }
      }
#else
      /* bbslog("**** %s should be removed\n", filename); */
/*
      unlink(filename);
*/
#endif

      {
        char *fp = strrchr(file, '/');
        if (fp != NULL)
        {
          *fp = '\0';
          cancel_article(BBSHOME, file, fp + 1);
          *fp = '/';
        }
      }
    }
  }
  if (*firstpath)
  {
    char **splitptr, *ngptr;
    newsfeeds_t *nf;
    splitptr = (char **) BNGsplit(GROUPS);
    for (ngptr = *splitptr; ngptr != NULL; ngptr = *(++splitptr))
    {
      if (*ngptr == '\0')
        continue;
      nf = (newsfeeds_t *) search_group(ngptr);
      if (nf == NULL)
        continue;
      if (nf->board == NULL)
        continue;
      if (nf->path == NULL)
        continue;
      feedfplog(nf, firstpathbase, 'D');
    }
  }
  return 0;
}


#if defined(PhoenixBBS) || defined(SecretBBS) || defined(PivotBBS) || defined(MapleBBS)
/* for PhoenixBBS's post article and cancel article */
#include "config.h"


char *
post_article(homepath, userid, board, writebody, pathname, firstpath)
  char *homepath;
  char *userid, *board;
  int (*writebody) ();
char *pathname, *firstpath;
{
  struct userec_t record;
  struct fileheader_t header;
  char *subject = SUBJECT;
  char index[MAXPATHLEN];
  static char name[MAXPATHLEN];
  char article[MAXPATHLEN];
  char buf[MAXPATHLEN], *ptr;
  FILE *fidx;
  int fh, bid;
  time_t now;
  int linkflag;
/* Ptt
  if(bad_subject(subject))  return NULL;
*/
  sprintf(index, "%s/.DIR", homepath);
  if ((fidx = fopen(index, "r")) == NULL)
  {
    if ((fidx = fopen(index, "w")) == NULL)
    {
      bbslog(":Err: Unable to post in %s.\n", homepath);
      return NULL;
    }
  }
  fclose(fidx);

  now = time(NULL);
  while (1)
  {
    sprintf(name, "M.%d.A", ++now);
    sprintf(article, "%s/%s", homepath, name);
    fh = open(article, O_CREAT | O_EXCL | O_WRONLY, 0644);
    if (fh >= 0)
      break;
    if (errno != EEXIST)
    {
      bbslog(" Err: can't writable or other errors\n");
      return NULL;
    }
  }

#ifdef DEBUG
  printf("post to %s\n", article);
#endif

  linkflag = 1;
  if (firstpath && *firstpath)
  {
    close(fh);
    unlink(article);

#ifdef DEBUGLINK
    bbslog("try to link %s to %s", firstpath, article);
#endif

    linkflag = link(firstpath, article);
    if (linkflag)
    {
      fh = open(article, O_CREAT | O_EXCL | O_WRONLY, 0644);
    }
  }
  if (linkflag)
  {
    if (writebody)
    {
      if ((*writebody) (fh, board, pathname) < 0)
        return NULL;
    }
    else
    {
      if (bbspost_write_post(fh, board, pathname) < 0)
        return NULL;
    }
    close(fh);
  }

  bzero((void *) &header, sizeof(header));

#ifndef MapleBBS
  strcpy(header.filename, name);
  strncpy(header.owner, userid, IDLEN);
  strncpy(header.title, subject, STRLEN);
  header.filename[STRLEN - 1] = 'M';
#else

  strcpy(header.filename, name);
  if (userid[IDLEN])
    strcpy(&userid[IDLEN], ".");
  strcpy(header.owner, userid);
  strncpy(header.title, subject, TTLEN);
  header.savemode = 'M';
  {
    struct tm *ptime;
    ptime = localtime(&datevalue);
    sprintf(header.date, "%2d/%02d", ptime->tm_mon + 1, ptime->tm_mday);
  }
#endif

  append_record(index, &header, sizeof(header));
  
  if((bid = getbnum(board)) > 0)
        touchbtotal(bid);
  return name;
}

/*
woju
Cross-fs rename()
*/

Rename(char* src, char* dst)
{
   char cmd[200];

   bbslog("Rename: %s -> %s\n", src, dst);
   if (rename(src, dst) == 0)
      return 0;

   sprintf(cmd, "/bin/mv %s %s", src, dst);
   return system(cmd);
}


cancelpost(fileheader_t *fhdr, char* boardname)
{
  int fd;
  char fpath[MAXPATHLEN];

  sprintf(fpath, BBSHOME "/boards/%c/%s/%s", boardname[0], boardname, fhdr->filename);
  if ((fd = open(fpath, O_RDONLY)) >= 0) {
    fileheader_t postfile;
    char fn2[MAXPATHLEN] = BBSHOME "/boards/d/deleted", *junkdir;

    stampfile(fn2, &postfile);
    memcpy(postfile.owner, fhdr->owner, IDLEN + TTLEN + 10);
    postfile.savemode = 'D';
    close(fd);
    Rename(fpath, fn2);
    strcpy(strrchr(fn2, '/') + 1, ".DIR");
    append_record(fn2, &postfile, sizeof(postfile));
  }
  else
     bbslog("cancelpost: %s opened error\n", fpath);
}


/* ---------------------------- */
/* new/old/lock file processing */
/* ---------------------------- */

typedef struct
{
  char newfn[MAXPATHLEN];
  char oldfn[MAXPATHLEN];
  char lockfn[MAXPATHLEN];
}      nol;


static void
nolfilename(n, fpath)
  nol *n;
  char *fpath;
{
  sprintf(n->newfn, "%s.new", fpath);
  sprintf(n->oldfn, "%s.old", fpath);
  sprintf(n->lockfn, "%s.lock", fpath);
}



int
delete_record(char *fpath, int size, int id)
{
  nol my;
  char abuf[512];
  int fdr, fdw, fd;
  int count;
  fileheader_t fhdr;

  nolfilename(&my, fpath);

  if ((fd = open(my.lockfn, O_RDWR | O_CREAT | O_APPEND, 0644)) == -1)
    return -1;
  flock(fd, LOCK_EX);

  if ((fdr = open(fpath, O_RDONLY, 0)) == -1)
  {

#ifdef  HAVE_REPORT
    report("delete_record failed!!! (open)");
#endif

    flock(fd, LOCK_UN);
    close(fd);
    return -1;
  }
  if ((fdw = open(my.newfn, O_WRONLY | O_CREAT | O_EXCL, 0644)) == -1)
  {
    flock(fd, LOCK_UN);

#ifdef  HAVE_REPORT
    report("delete_record failed!!! (open tmpfile)");
#endif

    close(fd);
    close(fdr);
    return -1;
  }
  count = 1;
  while (read(fdr, abuf, size) == size)
  {
    if (id == count) {
       memcpy(&fhdr, abuf, sizeof(fhdr));
       bbslog("delete_record: %d, %s, %s\n",  count, fhdr.owner, fhdr.title);
    }
    if (id != count++ && (write(fdw, abuf, size) == -1))
    {

      bbslog("delete_record: %s failed!!! (write)\n", fpath);
#ifdef  HAVE_REPORT
      report("delete_record failed!!! (write)");
#endif

      unlink(my.newfn);
      close(fdr);
      close(fdw);
      flock(fd, LOCK_UN);
      close(fd);
      return -1;
    }
  }
  close(fdr);
  close(fdw);
  if (Rename(fpath, my.oldfn) == -1 || Rename(my.newfn, fpath) == -1)
  {

#ifdef  HAVE_REPORT
    report("delete_record failed!!! (Rename)");
#endif

    flock(fd, LOCK_UN);
    close(fd);
    return -1;
  }
  flock(fd, LOCK_UN);
  close(fd);
  return 0;
}

cancel_article(homepath, board, file)
  char *homepath;
  char *board, *file;
{
  struct fileheader_t header;
  struct stat state;
  char dirname[MAXPATHLEN];
  char buf[MAXPATHLEN];
  long numents, size, time, now;
  int fd, lower, ent;


  if (file == NULL || file[0] != 'M' || file[1] != '.' ||
    (time = atoi(file + 2)) <= 0) {
    bbslog("cancel_article: invalid filename `%s`\n", file);
    return 0;
  }
  size = sizeof(header);
  sprintf(dirname, "%s/boards/%c/%s/.DIR", homepath, board[0], board);
  if ((fd = open(dirname, O_RDONLY)) == -1) {
    bbslog("cancel_article: open `%s` error\n", dirname);
    return 0;
  }
  fstat(fd, &state);
  ent = ((long) state.st_size) / size;
  lower = 0;
  while (1)
  {
    ent -= 8;
    if (ent <= 0 || lower >= 2)
      break;
    lseek(fd, size * ent, SEEK_SET);
    if (read(fd, &header, size) != size)
    {
      ent = 0;
      break;
    }
    now = atoi(header.filename + 2);
    lower = (now < time) ? lower + 1 : 0;
  }
  if (ent < 0)
    ent = 0;
  while (read(fd, &header, size) == size)
  {
    if (strcmp(file, header.filename) == 0)
    {
       if ((header.filemode & FILE_MARKED)
           || (header.filemode & FILE_DIGEST) || (header.owner[0] == '-'))
          break;
       delete_record(dirname, sizeof(fileheader_t), lseek(fd, 0, SEEK_CUR) / size);
       cancelpost(&header, board);
       break;
    }
    now = atoi(header.filename + 2);
    if (now > time)
      break;
  }
  close(fd);
  return 0;
}

#elif defined(PalmBBS)
# undef PATH XPATH
# undef HEADER XHEADER
#include "server.h"

char *
post_article(homepath, userid, board, writebody, pathname, firstpath)
  char *homepath;
  char *userid, *board;
  int (*writebody) ();
char *pathname, *firstpath;
{
  PATH msgdir, msgfile;
  static PATH name;

  READINFO readinfo;
  SHORT fileid;
  char buf[MAXPATHLEN];
  struct stat stbuf;
  int fh;

  strcpy(msgdir, homepath);
  if (stat(msgdir, &stbuf) == -1 || !S_ISDIR(stbuf.st_mode))
  {
    /* A directory is missing! */
    bbslog(":Err: Unable to post in %s.\n", msgdir);
    return NULL;
  }
  get_filelist_ids(msgdir, &readinfo);

  for (fileid = 1; fileid <= BBS_MAX_FILES; fileid++)
  {
    int oumask;
    if (test_readbit(&readinfo, fileid))
      continue;
    fileid_to_fname(msgdir, fileid, msgfile);
    sprintf(name, "%04x", fileid);

#ifdef DEBUG
    printf("post to %s\n", msgfile);
#endif

    if (firstpath && *firstpath)
    {

#ifdef DEBUGLINK
      bbslog("try to link %s to %s", firstpath, msgfile);
#endif

      if (link(firstpath, msgfile) == 0)
        break;
    }
    oumask = umask(0);
    fh = open(msgfile, O_CREAT | O_EXCL | O_WRONLY, 0664);
    umask(oumask);
    if (writebody)
    {
      if ((*writebody) (fh, board, pathname) < 0)
        return NULL;
    }
    else
    {
      if (bbspost_write_post(fh, board, pathname) < 0)
        return NULL;
    }
    close(fh);
    break;
  }

#ifdef CACHED_OPENBOARD
  {
    char *bname;
    bname = strrchr(msgdir, '/');
    if (bname)
      notify_new_post(++bname, 1, fileid, stbuf.st_mtime);
  }
#endif

  return name;
}

cancel_article(homepath, board, file)
  char *homepath;
  char *board, *file;
{
  PATH fname;

#ifdef  CACHED_OPENBOARD
  PATH bdir;
  struct stat stbuf;

  sprintf(bdir, "%s/boards/%c/%s", homepath, board[0], board);
  stat(bdir, &stbuf);
#endif

  sprintf(fname, "%s/boards/%c/%s/%s", homepath, board[0], board, file);
  unlink(fname);
  /* kill it now! the function is far small then original..  :) */
  /* because it won't make system load heavy like before */

#ifdef CACHED_OPENBOARD
  notify_new_post(board, -1, hex2SHORT(file), stbuf.st_mtime);
#endif
}

#else
error("You should choose one of the systems: PhoenixBBS, PowerBBS, or PalmBBS")
#endif

#else

receive_article()
{
}

cancel_article_front(msgid)
  char *msgid;
{
}
#endif