/*****************************************************************************
* Post for HPT (FTN NetMail/EchoMail Tosser)
*****************************************************************************
* Copyright (C) 1998-99
*
* Kolya Nesterov
*
* Fido:     2:463/7208.53
* Kiev, Ukraine
*
* This file is part of HPT.
*
* HPT is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* HPT is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HPT; see the file COPYING.  If not, write to the Free
* Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*****************************************************************************
* $Id: post.c,v 1.106.2.3 2004/03/19 06:03:52 sfpavel Exp $
*/

/* Revision log:
16.12.98 - first version, written at ~1:30, in the middle of doing
calculation homework on Theoretical Electrics, you understood ;)
18.12.98 - woops forgot copyright notice, minor fixes
tearline generation added
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <smapi/compiler.h>

#ifdef HAS_SYS_SYSEXITS_H
#include <sys/sysexits.h>
#endif
#ifdef HAS_SYSEXITS_H
#include <sysexits.h>
#endif

#include <smapi/progprot.h>

#include <fidoconf/fidoconf.h>
#include <fidoconf/common.h>
#include <fidoconf/xstr.h>
#include <fidoconf/afixcmd.h>
#include <fidoconf/recode.h>

#include <version.h>
#include <toss.h>
#include <post.h>
#include <global.h>
#include <version.h>
#include <areafix.h>
#include <hpt.h>
#include <scanarea.h>
#include <scan.h>


#if (defined(__EMX__) || defined(__MINGW32__)) && defined(__NT__)
/* we can't include windows.h for several reasons ... */
#define CharToOem CharToOemA
#endif

#define MAX_LINELEN 45
#define LINPERSECTION 150

#define ENCODE_BYTE(b) (((b) == 0) ? 0x60 : ((b) + 0x20))

void print_help(void) {
    fprintf(stdout,"\n   Post a message to area:\n");
    fprintf(stdout,"        hpt post [options] file\n\n");
    fprintf(stdout,"        options are:\n\n");
    fprintf(stdout,"        -nf \"name from\"\n");
    fprintf(stdout,"            message sender's name, if not included post  use\n");
    fprintf(stdout,"            sysop name (see fidoconfig)\n\n");
    fprintf(stdout,"        -nt \"name to\"\n");
    fprintf(stdout,"            message  receiver's  name,  if not included post use \"All\"\n\n");
    fprintf(stdout,"        -af \"address from\"\n");
    fprintf(stdout,"            message sender's address, if not  included  post\n");
    fprintf(stdout,"            use first system address (see fidoconfig)\n\n");
    fprintf(stdout,"        -at \"address to\"\n");
    fprintf(stdout,"            message receiver's address, *MUST BE PRESENT FOR NETMAIL*\n\n");
    fprintf(stdout,"         -s \"subject\"\n");
    fprintf(stdout,"            subject line, if not included then assumed to be empty\n\n");
    fprintf(stdout,"         -e \"echo area\"\n");
    fprintf(stdout,"            area to  post  echomail  message  into,  if  not\n");
    fprintf(stdout,"            included message is posted to netmail\n\n");
    fprintf(stdout,"         -z \"tearline\"\n");
    fprintf(stdout,"            tearline, if not included then assumed to be\n");
    fprintf(stdout,"            no tearline at all. Use -z \"\" to post with empty tearline\n\n");
    fprintf(stdout,"         -o \"origin\"\n");
    fprintf(stdout,"            origin, if not included then assumed to be name\n");
    fprintf(stdout,"            of station in config-file\n\n");
    fprintf(stdout,"         -f flags(s)\n");
    fprintf(stdout,"            flags  to  set  to the posted msg. possible ones\n");
    fprintf(stdout,"            are: pvt, crash, read, sent, att,  fwd,  orphan,\n");
    fprintf(stdout,"            k/s, loc, hld, xx2,  frq, rrq, cpt, arq, urq,\n");
    fprintf(stdout,"            kfs, tfs, dir, imm, cfm, npd;\n");
    fprintf(stdout,"            use it without trailing brackets like this:  pvt\n");
    fprintf(stdout,"            loc k/s\n\n");
    fprintf(stdout,"         -x export message to echo links\n\n");
    fprintf(stdout,"         -d erase input file after posting\n\n");
    fprintf(stdout,"         -u[size] uue-multipart posting\n");
    fprintf(stdout,"            size - number of lines per section(150 for default)\n\n");
    fprintf(stdout,"         -h get help\n\n");
    fprintf(stdout,"         file - text file to post into echo or \"-\" for stdin\n\n");
    exit(EX_OK);
}

void post(int c, unsigned int *n, char *params[])
{
    char *area = NULL, *tearl = NULL, *origin = NULL, *flags = NULL;
    FILE *text = NULL;
    FILE *tmpfile = NULL;
    char *tmpname = NULL;
    char *fname = NULL;
    s_area *echo = NULL;
    long attr;
    int sections=0;
    int part = 0;
    int linesPerSec=LINPERSECTION;
    struct _minf m;
    
    s_message msg;
    
    CHAR *textBuffer = NULL;
    
    int quit;
    int export=0;
    int erasef=0;
    int uuepost=0;
    int perms;
    
    time_t t = time (NULL);
    struct tm *tm;
    
    if (params[*n]!='\0' && params[*n][1]=='h') print_help();
    
    if (config==NULL) processConfig();
    if ( initSMAPI == -1 ) {
        /*  init SMAPI */
        initSMAPI = 0;
        m.req_version = 0;
        m.def_zone = (UINT16) config->addr[0].zone;
        if (MsgOpenApi(&m) != 0) {
            exit_hpt("MsgApiOpen Error",1);
        } /*endif */
    }
    
    memset(&msg, 0, sizeof(s_message));
    
    for (quit = 0;*n < (unsigned int)c && !quit; (*n)++) {
        if (*params[*n] == '-' && params[*n][1] != '\0') {
            switch(params[*n][1]) {
            case 'a':    /*  address */
                switch(params[*n][2]) {
                case 't':
                    string2addr(params[++(*n)], &(msg.destAddr));
                    break;
                case 'f':
                    string2addr(params[++(*n)], &(msg.origAddr));
                    break;
                default:
                    quit = 1;
                    break;
                }; break;
                case 'n':    /*  name */
                    switch(params[*n][2]) {
                    case 't':
                        msg.toUserName = (char *) safe_malloc(strlen(params[++(*n)]) + 1);
                        strcpy(msg.toUserName, params[*n]);
#ifdef __NT__
                        CharToOem(msg.toUserName, msg.toUserName);
#endif
                        break;
                    case 'f':
                        msg.fromUserName = (char *) safe_malloc(strlen(params[++(*n)]) + 1);
                        strcpy(msg.fromUserName, params[*n]);
#ifdef __NT__
                        CharToOem(msg.fromUserName, msg.fromUserName);
#endif
                        break;
                    default:
                        quit = 1;
                        break;
                    }; break;
                    case 'f':    /*  flags */
                        for ((*n)++; params[*n]!=NULL; (*n)++) {
                            char *p;
                            if ((attr=str2attr(params[*n])) != -1L)
                                msg.attributes |= attr;
                            else if ((p=extattr(params[*n])) != NULL)
                                xscatprintf(&flags, " %s", p);
                            else
                                break;
                        }
                        (*n)--;
                        break;
                    case 'e':    /*  echo name */
                        area = params[++(*n)];
                        echo = getArea(config, area);
                        if (echo == &(config->badArea)) {
                            w_log(LL_ERROR, "post: wrong area to post: %s" , area);
                            *n = (unsigned int)c;
                            quit = 1;
                        }
                        break;
                    case 's':    /*  subject */
                        msg.subjectLine = (char *) safe_malloc(strlen(params[++(*n)]) + 1);
                        strcpy(msg.subjectLine, params[*n]);
#ifdef __NT__
                        CharToOem(msg.subjectLine, msg.subjectLine);
#endif
                        break;
                    case 'x':    /*  export message */
                        export=1;
                        break;
                    case 'd':    /*  erase input file after posting */
                        erasef=1;
                        break;
                    case 'u':    /*  uue-multipart posting */
                        uuepost=1;
                        linesPerSec = atoi(params[(*n)]+2);
                        if(linesPerSec<10)
                            linesPerSec=LINPERSECTION;
                        break;
                    case 'z':
                        tearl = (char *) safe_malloc(strlen(params[++(*n)]) + 1);
                        strcpy(tearl, params[*n]);
#ifdef __NT__
                        CharToOem(tearl, tearl);
#endif
                        break;
                    case 'o':
                        origin = (char *) safe_malloc(strlen(params[++(*n)]) + 1);
                        strcpy(origin, params[*n]);
#ifdef __NT__
                        CharToOem(origin, origin);
#endif
                        break;
                    default:
                        w_log(LL_ERROR, "post: unknown switch %s", params[*n]);
                        quit = 1;
                        break;
            };
        } else if (textBuffer == NULL) {
            if (strcmp(params[*n], "-")) {
                if(fexist(params[*n])) 
                    text = fopen(params[*n], "rt");
            }
            else
                text = stdin;
            if (text != NULL) {
                if( uuepost && text != stdin)
                {
                    long lines = 1;
                    int	linelen;
                    int linecnt;
                    UCHAR inbuf [MAX_LINELEN];
                    UCHAR *inbytep;
                    char outbuf [5];
                    
                    xstrscat(&tmpname, config->tempOutbound, "hptucode.$$$",NULL);
                    text = freopen(params[*n], "rb", text);
                    tmpfile = fopen (tmpname, "wt");
                    if (tmpfile == NULL)
                    {
                        exit_hpt("Couldn't open tmpfile file", 1);
                    }
                    fname = GetFilenameFromPathname(params[*n]);
                    /* Write the 'begin' line, giving it a mode of 0600 */
		    perms = 0644;
#ifdef __UNIX__
		    {
			struct stat st;
			if (fstat(fileno(text), &st) == 0)
			    perms = st.st_mode & 0777;
		    }
#else
		    if (patimat(fname, "*.exe") ||
		        patimat(fname, "*.com"))
			perms = 0755;
#endif
                    fprintf (tmpfile, "begin %03o %s\n", perms, fname);
                    do
                    {
                        
                        linelen = fread (inbuf, 1, MAX_LINELEN, text);
                        fputc (ENCODE_BYTE (linelen), tmpfile);
                        
                        /* Encode the line */
                        for (linecnt = linelen, inbytep = inbuf;
                        linecnt > 0;
                        linecnt -= 3, inbytep += 3)
                        {
                            /* Encode 3 bytes from the input buffer */
                            outbuf [0] = ENCODE_BYTE ((inbytep [0] & 0xFC) >> 2);
                            outbuf [1] = ENCODE_BYTE (((inbytep [0] & 0x03) << 4) +
                                ((inbytep [1] & 0xF0) >> 4));
                            outbuf [2] = ENCODE_BYTE (((inbytep [1] & 0x0F) << 2) +
                                ((inbytep [2] & 0xC0) >> 6));
                            outbuf [3] = ENCODE_BYTE (inbytep [2] & 0x3F);
                            outbuf [4] = '\0';
                            
                            /* Write the 4 encoded bytes to the file */
                            fprintf (tmpfile, "%s", outbuf);
                        }
                        
                        fprintf (tmpfile, "\n");
                        lines++;
                    } while (linelen != 0);
                    
                    fprintf (tmpfile, "end\n");
                    lines++;
                    sections = (lines%linesPerSec==0) ?
                        lines/linesPerSec : lines/linesPerSec+1;
                    
                    fclose (tmpfile);
                    tmpfile = fopen (tmpname, "rt");
                    if (tmpfile == NULL)
                    {
                        exit_hpt("Couldn't open tmpfile file", 1);
                    }
                    textBuffer = safe_malloc(4*(MAX_LINELEN/3 + 1));
                }
                else
                {
                    int c, cursize=TEXTBUFFERSIZE;
                    /* reserve 512kb + 1 (or 32kb+1) text Buffer */
                    textBuffer = safe_malloc(cursize);
                    for (msg.textLength = 0;; msg.textLength++) {
                        if (msg.textLength >= cursize)
                            textBuffer = safe_realloc(textBuffer,
                                                      cursize += TEXTBUFFERSIZE);
                        c = getc(text);
                        if (c == EOF || c == 0) {
                            textBuffer[msg.textLength] = 0;
                            break;
                        }
                        textBuffer[msg.textLength] = (char)c;
                        if ('\r' == textBuffer[msg.textLength])
                            msg.textLength--;
                        if ('\n' == textBuffer[msg.textLength])
                            textBuffer[msg.textLength] = '\r';
                    }
                } /* endfor */
                while (!feof(text))
                    getc(text);
                if (strcmp(params[*n], "-"))
                    fclose(text);
                if (strcmp(params[*n], "-")&&erasef==1)
                    remove(params[*n]);
                if( uuepost && text == stdin)
                {
                    quit = 1;
                    nfree(textBuffer);
                }
            } else {
                w_log(LL_ERROR, "post: failed to open input file %s: %s", params[*n], strerror(errno));
                quit = 1;
            }
        } else {
            w_log(LL_ERROR, "post: several input files on cmd line");
            quit = 1;
        }
    }
    /*  won't be set in the msgbase, because the mail is processed if it were received */
    (*n)--; tm = localtime(&t);
    fts_time((char *)msg.datetime, tm);
    if ((msg.destAddr.zone != 0 || area) && (textBuffer != NULL) && !quit) {
        /*  Dumbchecks */
        if (msg.fromUserName == NULL)
            msg.fromUserName = safe_strdup(config->sysop);
        if (msg.toUserName == NULL)
            msg.toUserName = safe_strdup("All");
        if (msg.subjectLine == NULL)
            msg.subjectLine = safe_calloc(1, 1);
        
        msg.netMail = (char)(area == NULL);
        /*FIXME*/
        if (msg.netMail) echo=&(config->netMailAreas[0]);
        
        if (msg.origAddr.zone == 0) /*  maybe origaddr isn't specified ? */
            msg.origAddr = echo->useAka[0];
        
        w_log(LL_START, "Start posting...");
        part = 0; 
        do
        {
            
            if(!msg.netMail) memset(&msg.destAddr, '\0', sizeof(hs_addr));

            msg.text = createKludges(config,
                                     (area == 0) ? NULL : strUpper(area),
                                     &msg.origAddr,
                                     &msg.destAddr,
                                     versionStr);

            if ((export || !echo->fileName) && !config->disableTID)
                xscatprintf(&msg.text, "\001TID: %s\r", versionStr);

            if (flags) xscatprintf(&msg.text, "\001FLAGS %s\r", flags);

            if( uuepost )
            {
                /* char *res; */
                int i; 
                xscatprintf(&msg.text, "\rsection %d of %d of file %s < %s >\r\r",
                            part+1,sections,fname,versionStr);
                for(i = 0; i < linesPerSec; i++)
                {
                    char *res = readLine(tmpfile);
                    if(res)
                    {
                        xscatprintf(&msg.text,"%s\r",res);
                        nfree(res);
                    }
                    else
                    {
                        break;
                    }
                }
                xscatprintf(&msg.text,"section %d end\r",part+1);
                part++;
            }
            else
            {
                xstrcat((char **)(&(msg.text)), (char *)textBuffer);
            }

            if (msg.text[0] && msg.text[strlen(msg.text)-1] != '\r')
                xscatprintf(&msg.text, "\r");
            if (!msg.netMail || tearl)
                xscatprintf(&msg.text, "--- %s\r",
                  (tearl) ? tearl : (config->tearline) ? config->tearline : "");
            if (!msg.netMail || origin)
                xscatprintf(&msg.text, " * Origin: %s (%s)\r",
                  (origin) ? origin : (config->origin) ? config->origin : config->name,
                  aka2str(msg.origAddr));

            msg.textLength = strlen(msg.text);

            if ((msg.destAddr.zone + msg.destAddr.net +
                msg.destAddr.node + msg.destAddr.point)==0)
                w_log(LL_POSTING,
                "Posting msg from %u:%u/%u.%u -> %s in area: %s",
                msg.origAddr.zone, msg.origAddr.net,
                msg.origAddr.node, msg.origAddr.point,
                msg.toUserName,
                (area) ? area : echo->areaName);
            else w_log(LL_POSTING,
                "Posting msg from %u:%u/%u.%u -> %u:%u/%u.%u in area: %s",
                msg.origAddr.zone, msg.origAddr.net,
                msg.origAddr.node, msg.origAddr.point,
                msg.destAddr.zone, msg.destAddr.net,
                msg.destAddr.node, msg.destAddr.point,
                (area) ? area : echo->areaName);
            
            if (!export && echo->fileName) {
                msg.recode |= (REC_HDR|REC_TXT); /*  msg already in internal Charset */
                putMsgInArea(echo, &msg, 1, msg.attributes);
            }
            else {
                /*  recoding from internal to transport charSet */
                if (config->outtab != NULL) {
                    recodeToTransportCharset((CHAR*)msg.fromUserName);
                    recodeToTransportCharset((CHAR*)msg.toUserName);
                    recodeToTransportCharset((CHAR*)msg.subjectLine);
                    recodeToTransportCharset((CHAR*)msg.text);
                }
                if (msg.netMail) {
                    processNMMsg(&msg, NULL, NULL, 0, MSGLOCAL);
                }  else {
                    processEMMsg(&msg, *(echo->useAka), 1, (MSGSCANNED|MSGSENT|MSGLOCAL));
                }
            }
            nfree(msg.text);
        } while (part < sections);

        if (export) 
        {
            closeOpenedPkt();
            tossTempOutbound(config->tempOutbound);
            writeDupeFiles();
        }

        nfree(tearl); nfree(origin);
        if( uuepost )
        {
            fclose(tmpfile);
            remove(tmpname);
        }
        if ((config->echotosslog) && (!export)) {
            FILE *f=fopen(config->echotosslog, "a");
            if (f==NULL)
                w_log(LL_ERROR, "Could not open or create EchoTossLogFile.");
            else {
                fprintf(f, "%s\n", echo->areaName);
                fclose(f);
            }
        }
        w_log(LL_STOP, "End posting");
    }
    
    if (textBuffer == NULL && !quit) {
        w_log(LL_CRIT, "post: no input source specified");
        /* exit(EX_NOINPUT); */
    }
    else if (msg.destAddr.zone == 0 && !quit) {
        w_log(LL_CRIT,"post: attempt to post netmail msg without specifyng dest address");
        /* exit(EX_USAGE); */
    }
    nfree(textBuffer);
    freeMsgBuffers(&msg);

    /* deinit SMAPI */
    MsgCloseApi();
}
