/*
* READMAIL.C
*
* Written on 30-Jul-90 by jim nutt. Changes on 10-Jul-94 by John Dennis.
* Released to the public domain.
*
* Handles high-level message I/O.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#if defined(__MSC__) || defined(OS216)
#include <sys/types.h>
#include <sys/timeb.h>
#include <direct.h>
#endif
#if defined(MSDOS) && defined(__TURBOC__)
#include <dir.h>
#endif
#ifdef __WATCOMC__
#if defined(MSDOS) || defined(WINNT)
#include <direct.h>
#endif
#endif
#ifdef PACIFIC
#include <sys.h>
int bdos(int func, unsigned reg_dx, unsigned char reg_al);
#endif
#if defined(MSDOS) || (defined(WINNT) && (!defined(__MINGW32__)) && (!defined(__CYGWIN__)))
#include <dos.h>
#ifdef __TURBOC__
#include <dir.h>
#endif
#endif
#ifdef OS2
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif
#if defined(UNIX) || defined(__DJGPP__)
#include <unistd.h>
#endif
#ifdef __MINGW32__
#define chdir _chdir
#endif
#include "addr.h"
#include "nedit.h"
#include "msged.h"
#include "flags.h"
#include "memextra.h"
#include "strextra.h"
#include "config.h"
#include "quote.h"
#include "wrap.h"
#include "fecfg145.h"
#include "winsys.h"
#include "menu.h"
#include "dialogs.h"
#include "version.h"
#include "readmail.h"
#include "charset.h"
#include "date.h"
#include "echotoss.h"
#include "mctype.h"
#include "help.h"
#include "template.h"
#define rand_number(num) ((int) (((long) rand()) % (num)))
#define TEXTLEN 96
static void deleteCrapLine(LINE * crap);
static int is_sameaddr(ADDRESS * msg);
extern int set_rcvd; /* located in msged.c */
int read_verbatim = 0; /* see readmail.h for explanation */
#ifdef OS2
static unsigned long setDefaultDisk(unsigned short x);
#endif
static int changeDir(char *path);
char *do_softcrxlat(char *ptr)
{
char *p;
if (softcrxlat && ptr != NULL)
{
p = strchr(ptr, 0x8d);
while (p != NULL)
{
*p++ = softcrxlat;
p = strchr(p, 0x8d);
}
}
return ptr;
}
LINE *clearbuffer(LINE * buffer)
{
LINE *curline;
curline = buffer;
if (curline != NULL)
{
while (curline->next != NULL)
{
curline = curline->next;
if (curline->prev == NULL)
{
continue;
}
if (curline->prev->text != NULL)
{
xfree(curline->prev->text);
}
curline->prev->next = NULL;
xfree(curline->prev);
curline->prev = NULL;
}
if (curline != NULL)
{
if (curline->text)
{
xfree(curline->text);
}
curline->text = NULL;
xfree(curline);
}
}
return NULL;
}
static void KillTrailingLF(char *text)
{
char *s;
if (text == NULL)
{
return;
}
s = strchr(text, '\n');
if (s != NULL)
{
*s = '\0';
}
}
msg *readmsg(unsigned long n)
{
ADDRESS a;
LINE *l;
char *tokens[10];
int headerfin = 0, lastwasfromto = 0;
int afound = 0;
msg *m;
char *text;
char *t, *s;
char *ptmp;
char tmp[128];
int goteot = 0;
int gotsot = 0;
int had_codepage = 0;
LOOKUPTABLE *ltable = NULL;
int fmpt, topt;
int clevel = 0;
l = NULL;
memset(&a, 0, sizeof a);
m = MsgReadHeader(n, RD_ALL);
if (m == NULL)
{
read_verbatim = 0;
return NULL;
}
stripSoft = 1;
m->replyarea = NULL;
if (ST->enforced_charset != NULL || ST->input_charset != NULL)
{
memset(tokens, 0, sizeof(tokens));
if (ST->enforced_charset != NULL)
{
text = xstrdup(ST->enforced_charset);
}
else
{
text = xstrdup(ST->input_charset);
}
parse_tokens(text, tokens, 2);
if (tokens[1])
{
m->charset_level = atoi(tokens[1]);
}
else
{
m->charset_level = 2;
}
m->charset_name = xstrdup(tokens[0]);
ltable = get_readtable(m->charset_name, m->charset_level);
free(text);
}
else
{
m->charset_name = NULL;
m->charset_level = 0;
ltable = get_readtable("ASCII", 2);
}
while ((text = MsgReadText(n)) != NULL)
{
if (*text == '\n' || *text == '\0' || !stricmp(text, "Lines:"))
{
headerfin = 1; /* want to stop looking unix header info */
if (lastwasfromto && (!SW->shownotes) && (!read_verbatim))
{
release(text); /* skip the blank line after from: / to: */
lastwasfromto = 0;
continue;
}
}
lastwasfromto = 0;
if (*text == '\01')
{
switch (*(text + 1))
{
case 'A':
if (strncmp(text + 1, "AREA:", 5) != 0)
{
break;
}
s = text + 6;
while (m_isspace(*s))
{
s++;
}
release(m->replyarea);
m->replyarea = xstrdup(s);
KillTrailingLF(m->replyarea);
break;
case 'C':
if (ST->enforced_charset != NULL)
{
/* CHRS kludge is ignored if a special input charset
is enforced */
break;
}
if (strncmp(text + 1, "CHRS:", 5) == 0)
{
if (had_codepage)
break; /* @CODEPAGE is definitive override */
s = text + 6;
strcpy(tmp, s + 1);
}
else if (strncmp(text + 1, "CHARSET:", 8) == 0)
{
if (had_codepage)
break; /* @CODEPAGE is definitive override */
s = text + 9;
strcpy(tmp, s + 1);
}
else if (strncmp(text + 1, "CODEPAGE:", 9) == 0)
{
had_codepage = 1;
s = text + 10;
sprintf(tmp, "CP%s 2", s+1);
}
else
{
break;
}
memset(tokens, 0, sizeof(tokens));
parse_tokens(tmp, tokens, 2);
if (!tokens[1] || !tokens[1][0])
{
clevel = 2;
/* Assume level 2 as default. The CHARSET kludge has no
level at all, and some broken message readers omit
the level also for the CHRS kludge. */
}
else
{
clevel = atoi(tokens[1]);
}
if ( have_readtable(tokens[0], clevel) ||
ST->input_charset == NULL /* user wants no assumptions */)
{
release(m->charset_name);
m->charset_name = xstrdup(tokens[0]);
m->charset_level = clevel;
ltable = get_readtable(m->charset_name,
m->charset_level);
}
break;
case 'M':
if (strncmp(text + 1, "MSGID:", 6) != 0)
{
break;
}
s = text + 7;
while (m_isspace(*s))
{
s++;
}
release(m->msgid);
m->msgid = xstrdup(s);
KillTrailingLF(m->msgid);
break;
case 'R':
if (strncmp(text + 1, "REPLY:", 6) != 0)
{
break;
}
s = text + 7;
while (m_isspace(*s))
{
s++;
}
if (arealist[SW->area].msgtype != QUICK)
{
release(m->reply);
m->reply = xstrdup(s);
KillTrailingLF(m->reply);
}
break;
case 'E':
if (strncmp(text + 1, "EOT:", 4) == 0)
{
goteot = 1;
if (gotsot && !(SW->showseenbys ||
SW->shownotes || read_verbatim))
{
m->soteot = 1;
}
}
break;
case 'S':
if (strncmp(text + 1, "SOT:", 4) == 0)
{
gotsot = 1;
stripSoft = 0;
}
break;
case 'F':
if (strncmp(text + 1, "FMPT", 4) == 0)
{
s = text + 5;
m->from.point = atoi(s + 1);
}
else if (strncmp(text + 1, "FLAGS", 5) == 0)
{
parseflags(text + 7, m);
}
break;
case 'T':
if (strncmp(text + 1, "TOPT", 4) == 0)
{
s = text + 5;
m->to.point = atoi(s + 1);
}
else if (strncmp(text + 1, "TZUTC:", 6) == 0)
{
s = text + 7;
while (m_isspace(*s))
{
s++;
}
m->timezone = (atoi(s) % 100) + ((atoi(s) / 100) * 60);
m->has_timezone = 1;
}
break;
case 'D':
if (strncmp(text + 1, "DOMAIN", 6) != 0)
{
break;
}
s = text + 7;
strcpy(tmp, s);
memset(tokens, 0, sizeof(tokens));
parse_tokens(tmp, tokens, 4);
if (!tokens[3])
{
break;
}
memset(&a, 0, sizeof a);
a = parsenode(tokens[1]);
if (a.fidonet)
{
release(m->to.domain);
m->to = a;
m->to.domain = xstrdup(tokens[0]);
}
memset(&a, 0, sizeof a);
a = parsenode(tokens[3]);
if (a.fidonet)
{
release(m->from.domain);
m->from = a;
m->from.domain = xstrdup(tokens[2]);
}
break;
case 'I':
fmpt = m->from.point; /* INTL kludge is not 4D */
topt = m->to.point;
if (strncmp(text + 1, "INTL", 4) != 0)
{
break;
}
s = text + 5;
strcpy(tmp, s + 1);
memset(tokens, 0, sizeof(tokens));
parse_tokens(tmp, tokens, 2);
if (!tokens[1])
{
break;
}
memset(&a, 0, sizeof a);
a = parsenode(tokens[0]);
if (a.fidonet)
{
release(m->to.domain);
m->to = a;
}
memset(&a, 0, sizeof a);
a = parsenode(tokens[1]);
if (a.fidonet)
{
release(m->from.domain);
m->from = a;
}
m->to.point = topt; /* restore the point numbers */
m->from.point = fmpt;
break;
}
if ((!SW->shownotes) && (!read_verbatim))
{
release(text);
continue;
}
}
if (*text == 'S')
{
if (strncmp(text, "SEEN-BY:", 8) == 0 &&
!(SW->showseenbys || SW->shownotes || read_verbatim) &&
(!gotsot || goteot))
{
release(text);
continue;
}
}
if (goteot && *text == '\n' &&
!(SW->showseenbys || SW->shownotes || read_verbatim))
{
release(text);
continue;
}
/* from Roland Gautschi */
if (SW->tabexpand && strchr(text, '\t') != NULL)
{
do
{
ptmp = xstrdup(text);
release(text);
text = xmalloc(strlen(ptmp) + SW->tabsize);
t = strchr(ptmp, '\t');
/* characters before \t */
strncpy(text, ptmp, (size_t) (t - ptmp));
/* replace \t with spaces */
memset(text + (size_t) (t - ptmp), ' ', SW->tabsize);
/* copy the rest */
strcpy(text + (size_t) (t - ptmp) + SW->tabsize, t + 1);
xfree(ptmp);
}
while (strchr(text, '\t') != NULL);
}
if (CurArea.echomail)
{
if (afound == 0 && strlen(text) > 10 && *(text + 1) == '*')
{
if (!strncmp(text, " * Origin:", 10))
{
/* probably the origin line */
s = strrchr(text, '(');
if (s != NULL)
{
while (*s && !m_isdigit(*s) && *s != ')')
{
s++;
}
if (m_isdigit(*s))
{
m->from = parsenode(s);
}
}
else
{
m->from.notfound = 1;
}
}
}
}
if (strncmp(text, "---", 3) == 0 && strncmp(text, "----", 4) != 0 &&
!(SW->showtearlines || SW->shownotes || read_verbatim) &&
(!gotsot || goteot))
{
release(text);
continue;
}
if (strncmp(text, " * Origin:", 10) == 0 &&
!(SW->showorigins || SW->shownotes || read_verbatim) &&
(!gotsot || goteot))
{
release(text);
continue;
}
if ((CurArea.uucp || CurArea.news) && headerfin == 0)
{
char *s;
if (CurArea.uucp)
{
if (strncmpi(text, "To: ", 4) == 0)
{
char *cpdomain, *cpname;
s = strchr(text, ' ') + 1;
parse_internet_address(s, &cpdomain, &cpname);
m->to.fidonet = 0;
m->to.internet = 0;
m->to.bangpath = 0;
m->to.notfound = 0;
if (strchr(cpdomain, '@') != NULL)
{
m->to.internet = 1;
}
else
{
m->to.bangpath = 1;
}
release(m->to.domain);
m->to.domain = cpdomain;
release (m->isto);
m->isto = cpname;
lastwasfromto = 1;
if (!(SW->shownotes || read_verbatim))
{
release(text);
continue;
}
}
}
if ( (strncmpi(text, "From: ", 6) == 0) ||
(strncmpi(text, "Reply-To: ", 10) == 0) ) /* TE 03/01/98 */
{
char *cpname, *cpdomain;
s = strchr(text,' ') + 1;
parse_internet_address(s, &cpdomain, &cpname);
/* do not adopt a probably non-existent Reply-To User Name */
if (strncmpi(text, "Reply-To: ", 10) != 0)
{
release(m->isfrom);
m->isfrom = cpname;
}
m->from.fidonet = 0;
m->from.internet = 0;
m->from.bangpath = 0;
m->from.notfound = 0;
afound = 1;
if (strchr(cpdomain, '@') != NULL)
{
m->from.internet = 1;
}
else
{
m->from.bangpath = 1;
}
release(m->from.domain);
m->from.domain = cpdomain;
lastwasfromto = 1;
if (!(SW->shownotes || read_verbatim))
{
release(text);
continue;
}
}
}
if (*text != '\01' || SW->shownotes || read_verbatim)
{
if (l == NULL)
{
l = xcalloc(1, sizeof(LINE));
m->text = l;
l->next = l->prev = NULL;
}
else
{
l->next = xcalloc(1, sizeof(LINE));
if (l->next == NULL)
{
xfree(text);
break;
}
l->next->next = NULL;
l->next->prev = l;
l = l->next;
}
l->block = 0;
if (!read_verbatim)
{
/* character set translation: import to local charset */
l->text = translate_text(text, ltable);
strip_control_chars(l->text);
}
else
{
l->text = xstrdup(text);
}
release(text); text = l->text;
l->hide = (*text == '\01');
if (is_quote(text))
{
l->quote = 1;
}
else
{
l->quote = 0;
}
if (l->quote)
{
if (strlen(l->text) > maxx)
{
wrap(l, 1, maxy, maxx);
while (l->next)
{
l = l->next;
}
}
}
else
{
if (*text != '\01' && *text != '\n' && strlen(text) > maxx)
{
wrap(l, 1, maxy, maxx);
while (l->next)
{
l = l->next;
}
}
}
}
else
{
release(text);
}
}
MsgClose();
if (!read_verbatim) /* recode the header, important for Russian users */
{
char *tmp;
tmp = translate_text(m->isfrom, ltable);
release(m->isfrom); m->isfrom = tmp;
strip_control_chars(m->isfrom);
tmp = translate_text(m->isto, ltable);
release(m->isto); m->isto = tmp;
strip_control_chars(m->isto);
tmp = translate_text(m->subj, ltable);
release(m->subj); m->subj = tmp;
strip_control_chars(m->subj);
}
if (set_rcvd)
{
checkrcvd(m, n);
}
read_verbatim = 0;
return m;
}
/*
* checkrcvd(); Checks to see if a message has been recieved. If so,
* reads the message header again (avoiding translations done in the
* original readinf process) and then writes the header to the message
* base.
*/
void checkrcvd(msg * m, unsigned long n)
{
msg *mn;
int set_received = 0, j;
if (m->attrib.rcvd)
{
return;
}
m->times_read++;
if (is_sameaddr(&m->to))
{
if (SW->receiveallnames)
{
for (j = 0; j < MAXUSERS && (!set_received); j++)
{
if (user_list[j].name != NULL &&
stricmp(user_list[j].name, m->isto) == 0)
{
set_received = 1;
}
}
}
else
{
if (stricmp(ST->username, m->isto) == 0)
{
set_received = 1;
}
}
}
if (set_received)
{
mn = MsgReadHeader(n, RD_HEADER);
if (mn == NULL)
{
return;
}
mn->attrib.rcvd = 1;
m->attrib.rcvd = 1;
m->newrcvd = 1;
mn->times_read++;
MsgWriteHeader(mn, WR_HEADER);
dispose(mn);
}
}
static int is_sameaddr(ADDRESS * msg)
{
int j;
if (!CurArea.netmail)
{
/* we only care about the address in netmail areas */
return 1;
}
if (!SW->receivealladdr)
{
if (msg->zone != CurArea.addr.zone ||
msg->net != CurArea.addr.net ||
msg->node != CurArea.addr.node ||
msg->point != CurArea.addr.point)
{
return 0;
}
return 1;
}
else
{
for (j = 0; j < SW->aliascount; j++)
{
if (alias[j].zone == msg->zone &&
alias[j].net == msg->net &&
alias[j].node == msg->node &&
alias[j].point == msg->point)
{
return 1;
}
}
return 0;
}
}
/*
* Clears a message only - wipes the slate clean.
*/
void clearmsg(msg * m)
{
if (m == NULL)
{
return;
}
/* kill the header stuff */
release(m->reply);
release(m->msgid);
release(m->isfrom);
release(m->isto);
release(m->subj);
release(m->to.domain);
release(m->from.domain);
release(m->replyarea);
release(m->charset_name);
if (m->text)
{
/* kill the text */
m->text = clearbuffer(m->text);
}
/* clear the whole lot */
memset(m, 0, sizeof *m);
/* set the defaults */
m->attrib.priv = CurArea.priv;
m->attrib.crash = CurArea.crash;
m->attrib.hold = CurArea.hold;
m->attrib.direct = CurArea.direct;
m->attrib.killsent = CurArea.killsent;
m->attrib.local = 1;
}
int setcwd(char *path)
{
char *p;
p = strchr(path, ':');
if (p == NULL)
{
p = path;
}
if (*p == ':')
{
p++;
#if defined(OS2)
setDefaultDisk((unsigned short)(toupper(*path) - 'A' + 1));
#elif defined(MSDOS) && !defined(__FLAT__)
bdos(14, toupper(*path) - 'A', 0);
#elif defined(__FLAT__) && defined(MSDOS)
{
unsigned dummy;
_dos_setdrive(toupper(*path) - 'A' + 1, &dummy);
}
#endif
}
return changeDir(p);
}
int isleap(int year)
{
return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0);
}
unsigned long unixtime(const struct tm *tm)
{
int year, i;
unsigned long result;
result = 0UL;
year = tm->tm_year + 1900;
/* traverse through each year */
for (i = 1970; i < year; i++)
{
result += 31536000UL; /* 60s * 60m * 24h * 365d = 31536000s */
if (isleap(i))
{
/* it was a leap year; add a day's worth of seconds */
result += 86400UL; /* 60s * 60m * 24h = 86400s */
}
}
/*
* Traverse through each day of the year, adding a day's worth
* of seconds each time.
*/
for (i = 0; i < tm->tm_yday; i++)
{
result += 86400UL; /* 60s * 60m * 24h = 86400s */
}
/* now add the number of seconds remaining */
result += 3600UL * tm->tm_hour;
result += 60UL * tm->tm_min;
result += (unsigned long) tm->tm_sec;
return result;
}
unsigned long sec_time(void)
{
time_t now;
struct tm *tm;
now = time(NULL);
tm = localtime(&now);
return unixtime(tm);
}
/*
* Inserts a line after the passed line and returns a pointer to it.
*/
LINE *InsertAfter(LINE * l, char *text)
{
LINE *nl;
nl = xcalloc(1, sizeof(LINE));
nl->text = xstrdup(text);
if (l == NULL)
{
return nl;
}
nl->next = l->next;
nl->prev = l;
l->next = nl;
if (nl->next)
{
nl->next->prev = nl;
}
return nl;
}
/* Translate a token for the origin line */
static void trans_token(char *line, int maxlen, msg *m)
{
char *old, *cp, *cp2, *atm;
int linelen = 0, digits;
struct tm tm;
long count = -1, quote_count = -1, temp;
if (line == NULL || maxlen < 1)
{
return;
}
cp2 = cp = old = xstrdup(line);
*line = 0;
while (cp != NULL && maxlen - 1 > linelen)
{
cp = strchr(cp2, '@');
if (cp != NULL)
{
*cp = '\0';
}
linelen += sprintf(line + linelen, "%-.*s",
maxlen - 1 - linelen, cp2);
if (cp == NULL)
{
break;
}
cp++;
switch (toupper(*cp))
{
case '@': /* escaped @ character */
strcat(line + (linelen), "@");
linelen++;
break;
case 'N': /* insert full name */
if (m->isto != NULL)
{
linelen += sprintf(line + linelen, "%-.*s",
maxlen - 1 - linelen,
m->isto);
}
break;
case 'A': /* insert reply area name */
linelen += sprintf(line + linelen, "%-.*s",
maxlen - 1 - linelen,
CurArea.tag);
break;
case 'S': /* insert subject */
if (m->subj != NULL)
{
linelen += sprintf(line + linelen, "%-.*s",
maxlen - 1 - linelen,
m->subj);
}
break;
case 'Y': /* insert user's full name */
linelen += sprintf(line + linelen, "%-.*s",
maxlen - 1 - linelen,
m->isfrom);
break;
case 'I': /* insert message size */
if (count == -1)
{
count_bytes(m->text, &count, "e_count);
}
for (digits = 0, temp = count; temp !=0; digits++)
{
temp = temp / 10;
}
if (!digits)
{
digits++;
}
if (maxlen - 1 - linelen >= digits)
{
linelen += sprintf(line + linelen, "%ld", count);
}
else
{
linelen = maxlen;
}
break;
case 'Q': /* in sert quote % */
if (count == -1)
{
count_bytes(m->text, &count, "e_count);
}
if (maxlen - 1 - linelen >= ((quote_count == count) ? 4 : 3))
{
linelen += sprintf(line + linelen, "%02ld%%",
quote_count * 100 / count);
}
else
{
linelen = maxlen;
}
break;
case 'D': /* insert date */
tm = *localtime(&m->timestamp);
switch (toupper(*(++cp)))
{
case 'D': /* day */
if (maxlen - 1 - linelen >= 2)
{
linelen += sprintf(line + linelen, "%2.2d",
tm.tm_mday);
}
else
{
linelen = maxlen;
}
break;
case 'W': /* week day (mon, tue, ...) */
if (maxlen - 1 - linelen >= 3)
{
atm = atime(m->timestamp);
linelen += sprintf(line + linelen, "%3.3s", atm);
}
else
{
linelen = maxlen;
}
break;
case 'M': /* month (jan, feb, .....) */
if (maxlen - 1 - linelen >= 3)
{
atm = atime(m->timestamp);
linelen += sprintf(line + linelen, "%3.3s",
atm + 4);
}
else
{
linelen = maxlen;
}
break;
case 'Y': /* two digit year */
if (maxlen - 1 - linelen >= 2)
{
linelen += sprintf(line + linelen, "%2.2d",
tm.tm_year % 100);
}
else
{
linelen = maxlen;
}
break;
case 'C': /* century: 1900 - 1999: 20, etc. */
if (maxlen - 1 - linelen >= 2)
{
linelen += sprintf(line + linelen, "%2.2d",
(tm.tm_year / 100) + 20);
}
else
{
linelen = maxlen;
}
break;
case '4': /* four digit year */
if (maxlen - 1 - linelen >= 4)
{
linelen += sprintf(line + linelen, "%4.4d",
tm.tm_year + 1900);
}
else
{
linelen = maxlen;
}
break;
default: /* date in format DD Mon YY */
cp--;
if (maxlen - 1 - linelen >= 9)
{
linelen += sprintf(line + linelen, "%-9.9s",
mtime(m->timestamp));
}
else
{
linelen = maxlen;
}
break;
}
break;
case 'T': /* insert time hh:mm:ss */
if (maxlen - 1 - linelen >= 8)
{
tm = *localtime(&m->timestamp);
linelen += sprintf(line + linelen, "%2.2d:%2.2d:%2.2d",
tm.tm_hour, tm.tm_min, tm.tm_sec);
}
else
{
linelen = maxlen;
}
break;
case 'F': /* First Name in TO: Field of new message */
linelen += sprintf(line + linelen, "%-.*s",
maxlen - linelen - 1, firstname(m->isto));
break;
case 'L': /* Last Name in TO: Field of new message */
linelen += sprintf(line + linelen, "%-.*s",
maxlen - linelen - 1, lastname(m->isto));
break;
} /* end switch */
cp2 = ++cp;
} /* end while */
xfree(old);
}
/*
* Gets the origin line to use.
*/
static char *GetFastEchoOrigin(int board)
{
FILE *fp;
CONFIG feconfig;
Area fearea;
ExtensionHeader feexthdr;
static OriginLines feorigin;
int cnt, found;
fp = fopen(ST->fecfgpath, "rb");
if (fp == NULL)
{
return NULL;
}
if (fread(&feconfig, sizeof feconfig, 1, fp) != 1)
{
return NULL;
}
fseek(fp, sizeof feconfig + feconfig.offset + feconfig.NodeCnt *
feconfig.NodeRecSize, SEEK_SET);
do
{
if (fread(&fearea, sizeof fearea, 1, fp) != 1)
{
return NULL;
}
}
while (board != fearea.board);
cnt = 0;
found = 0;
fseek(fp, sizeof feconfig, SEEK_SET);
while (!found)
{
fread(&feexthdr, sizeof feexthdr, 1, fp);
if (feexthdr.type == EH_ORIGINS)
{
do
{
if (fread(&feorigin, sizeof feorigin, 1, fp) != 1)
{
return NULL;
}
cnt++;
}
while (cnt <= (fearea.flags.origin));
found = 1;
}
else
{
fseek(fp, feexthdr.offset, SEEK_CUR);
}
}
fclose(fp);
if (feorigin.line && *feorigin.line != '\0')
{
return feorigin.line;
}
else
{
return NULL;
}
}
void GetOrigin(char *origin)
{
FILE *fp;
char path[255];
if (n_origins > 1) /* origin shuffling */
{
release(ST->origin);
ST->origin = xstrdup(origins[rand_number(n_origins)]);
}
if (!SW->override)
{
if (CurArea.msgtype == SQUISH)
{
sprintf(path, "%s.sqo", CurArea.path);
}
else
{
sprintf(path, "%s\\origin", CurArea.path);
}
fp = fopen(path, "r");
if (fp != NULL)
{
int i, orgnum, orgcnt;
i = 0;
orgnum = 0;
orgcnt = 0;
do
{
*origin = '\0';
fgets(origin, 65, fp);
if (*origin)
{
orgcnt++;
}
}
while (!feof(fp));
orgnum = rand_number(orgcnt) + 1;
rewind(fp);
do
{
*origin = '\0';
fgets(origin, 65, fp);
if (*origin)
{
i++;
}
if (i == orgnum)
{
break;
}
}
while (!feof(fp));
fclose(fp);
}
else
{
if (ST->origin != NULL)
{
sprintf(origin, "%s", ST->origin);
}
else
{
sprintf(origin, "%s", ST->username);
}
if (CurArea.msgtype == QUICK && areas_type == FASTECHO)
{
char *feorigin;
feorigin = GetFastEchoOrigin(CurArea.board);
if (feorigin != NULL)
{
sprintf(origin, "%s", feorigin);
}
}
}
}
else
{
if (ST->origin != NULL)
{
sprintf(origin, "%s", ST->origin);
}
else
{
sprintf(origin, "%s", ST->username);
}
}
striptwhite(origin);
}
static void InvalidateKludges(msg * m)
{
LINE *line;
line = m->text;
while (line != NULL)
{
char *p;
p = line->text;
if (p != NULL)
{
if (*p == '\01')
{
*p = '@';
}
else if (strstr(p, "SEEN-BY: ") == p)
{
*(p + 4) = '+';
}
else if (strstr(p, "---\n") == p || strstr(p, "--- ") == p ||
strstr(p, " * Origin: ") == p)
{
*(p + 1) = '+';
}
}
line = line->next;
}
}
static void StripKludges(msg * m, int *got_originline, char *origin,
int *got_tearline, char *tearline)
{
LINE *line;
int got_origin, got_tear, got_text;
/* traverse forwards through linked list until the end */
line = m->text;
while (line->next != NULL)
{
line = line->next;
}
/* now go backwards, removing any unwanted stuff */
got_origin = 0;
got_tear = 0;
got_text = 0;
while (line != NULL)
{
char *p;
p = line->text;
if (p != NULL)
{
if (strstr(p, "\01MSGID: ") == p || strstr(p, "\01REPLY: ") == p ||
strstr(p, "\01FLAGS ") == p || strstr(p, "\01PID: ") == p ||
strstr(p, "\01SOT:") == p || strstr(p, "\01EOT:") == p ||
strstr(p, "\01CHRS: ") == p || strstr(p, "\01TZUTC:") == p ||
strstr(p, "\01CODEPAGE:") == p)
{
*p = '\0';
}
else if (CurArea.netmail)
{
if (strstr(p, "\01INTL ") == p || strstr(p, "\01TOPT ") == p ||
strstr(p, "\01FMPT ") == p || strstr(p, "\01DOMAIN ") == p ||
strstr(p, "\01Via ") == p)
{
*p = '\0';
}
}
else if (!CurArea.netmail)
{
if (strstr(p, "SEEN-BY: ") == p || strstr(p, "\01PATH: ") == p)
{
*p = '\0';
}
else if (!got_text && !got_origin && strstr(p, " * Origin: ") == p)
{
*p = '\0';
got_origin = 1;
if (origin != NULL) /* preserve an user-edited origin */
{
strncpy (origin, p + 11, 79);
origin[79] = 0;
p = strrchr(origin, '(');
if (p != NULL)
{
*p = '\0';
}
striptwhite(origin);
}
}
else if (!got_text && !got_tear && (strstr(p, "---\n") == p ||
strstr(p, "--- ") == p))
{
/* preserve an user-edited tearline */
if (tearline != NULL)
{
strncpy (tearline, p+4, SW->xxltearline ? 75 : 31);
tearline[SW->xxltearline ? 75 : 31] = 0;
striptwhite (tearline);
}
*p = '\0';
got_tear = 1;
}
else if (!got_text && (*p == '\n' || *p == '\r'))
{
*p = '\0';
}
else if (!got_text && *p != '\0' && *p != '\n' && *p != '\r')
{
got_text = 1;
}
}
}
line = line->prev;
}
*got_originline = got_origin;
*got_tearline = got_tear;
}
char *space_after_period(LINE *ln)
{
int snt_count, spc_count;
snt_count = 0;
spc_count = 0;
while (ln != NULL)
{
char *p;
p = ln->text;
while (*p != '\0')
{
if (*p == '.' && *(p + 1) == ' ')
{
snt_count++;
spc_count++;
if (*(p + 2) == ' ')
{
spc_count++;
}
}
p++;
}
ln = ln->prev;
}
if (snt_count != 0 && spc_count / snt_count == 2)
{
return " ";
}
else
{
return " ";
}
}
static int askgate(char *which)
{
char str[80];
int rc;
sprintf(str, "Send this message via %s gate?", which);
while (1)
{
rc = ChoiceBox("", str, "Yes", "No",
strcmp(which, "zone") ? NULL : "Help");
switch(rc)
{
case ID_ONE:
return 1;
case ID_TWO:
return 0;
case ID_THREE:
if (!strcmp(which, "zone"))
{
DoHelp(6);
}
break;
}
}
}
/*
* Sequence of events:
*
* Original address is saved (what is displayed);
* Domain gates are checked for and address is modified if one found;
* if no domain gates, search for UUCP gate && mod address if found.
* check INTL
* check MSGID
* check REPLY
* do PID if one,
* If we found domain gate, do DOMAIN with original saved addresses.
* if we found UUCP, then do a "to:" kludge.
*/
/*
* Writes a message to disk.
*/
int writemsg(msg * m)
{
LINE *curr, *l, *ufrom, *uto, *ureplyto, *xblank,
*xtear, *xorigin, *ublank;
ADDRESS to;
ADDRESS from;
unsigned long n; /* UMSGID msgnum */
unsigned long length; /* length in bytes of the message */
char text[255]; /* buffer useage */
char origin[255]; /* out origin line */
char tearline[76]; /* user-defined output tearline */
char *uucp_from; /* saved UUCP from address */
char *uucp_to; /* saved UUCP to address */
int domain_gated, uucp_gated;
int do_zonegate = 0;
char *s;
int i, abortWrite, got_origin, got_tear, ctrl;
static unsigned long now = 0L;
LOOKUPTABLE *ltable = NULL;
int write_chrs_kludge;
const int tear_max = SW->xxltearline ? 79 : 35;
char *temptext;
domain_gated = 0;
uucp_gated = 0;
length = 0;
ctrl = 1;
n = m->msgnum;
curr = NULL;
uto = NULL;
ureplyto = NULL;
ufrom = NULL;
uucp_from = NULL;
uucp_to = NULL;
xorigin = NULL;
xtear = NULL;
xblank = NULL;
ublank = NULL;
#if 0
while (MsgLockArea() == -1) /* Lock the Msg area for writing */
{
int ret;
ret = ChoiceBox(" Error! ", "Could not write message!",
"Retry", "Cancel", NULL);
if (ret == ID_TWO)
{
return FALSE;
}
}
#endif
if (now == 0L)
{
now = sec_time();
}
if (m->invkludges)
{
InvalidateKludges(m);
got_origin = got_tear = 0;
}
else if (!m->rawcopy)
{
StripKludges(m, &got_origin, origin, &got_tear, tearline);
}
ltable = get_writetable(CurArea.eightbits ? ST->output_charset : "ASCII",
&write_chrs_kludge);
if (!m->rawcopy)
{
/* save the original address */
to = m->to;
from = m->from;
/* do domain gating */
if (SW->domains &&
m->to.domain &&
m->from.domain &&
stricmp(m->from.domain, m->to.domain) &&
(SW->gate == GDOMAINS || SW->gate == BOTH ||
(SW->gate == GASK && askgate("domain"))) &&
!m->to.internet /* in this case, "domain" string is e-mail addr */
)
{
/*
* If we have two domains and they're different, then we want to
* gate. If we have a to: domain and no from: domain, then we
* still may want to gate. If we don't have a to: domain, then
* we don't want to gate the message (we assume it's destined to
* our own network).
*/
for (i = 0; i < SW->domains; i++)
{
if (!stricmp(domain_list[i].domain, m->to.domain))
{
domain_gated = 1;
if (m->attrib.crash || m->attrib.direct ||
m->attrib.hold || m->attrib.immediate)
{
int ret;
ret = ChoiceBox(" Direct Message ",
" Direct Message "
"(crash, dir, imm or hold) to?",
"Domain Gate", "Destination Node", NULL);
if (ret == ID_ONE)
{
m->to = domain_list[i];
if (domain_list[i].domain)
{
m->to.domain = xstrdup(domain_list[i].domain);
}
}
}
else
{
m->to = domain_list[i];
if (domain_list[i].domain)
{
m->to.domain = xstrdup(domain_list[i].domain);
}
}
break;
}
}
}
/* do uucp gating */
if (m->to.internet || m->to.bangpath)
{
uucp_gated = 1;
uucp_to = m->to.domain;
if (!CurArea.echomail) /* don't change adresses */
{ /* in echomail areas */
m->to = uucp_gate;
}
if (uucp_gate.domain)
{
m->to.domain = xstrdup(uucp_gate.domain);
}
if (CurArea.netmail)
{
/* AKA-Matching for UUCP gated messages */
akamatch(&(m->from), &(m->to));
}
}
if (m->from.internet || m->from.bangpath)
{
uucp_gated = 1;
uucp_from = xstrdup(m->from.domain);
if (!CurArea.echomail) /* don't change adresses */
{ /* in echomail areas */
m->from = uucp_gate;
}
if (uucp_gate.domain)
{
m->from.domain = xstrdup(uucp_gate.domain);
}
}
/* do the netmail stuff */
/* uccp is an ADD ON flag for netmail, not a replacement,
so I commented it out below, because it lead to INTL
kludges in echomail areas and the like */
if (CurArea.netmail /* || CurArea.uucp */ )
{
/* print INTL kludge */
if (m->from.zone != m->to.zone || m->from.zone != thisnode.zone)
{
sprintf(text, "\01INTL %d:%d/%d %d:%d/%d\r", m->to.zone,
m->to.net, m->to.node, m->from.zone, m->from.net, m->from.node);
curr = InsertAfter(curr, text);
}
/* print FMPT / TOPT kludges */
if (m->to.point)
{
sprintf(text, "\01TOPT %d\r", m->to.point);
curr = InsertAfter(curr, text);
}
if (! (!m->attrib.direct && !m->attrib.crash && m->from.point &&
SW->pointnet != 0 && CurArea.netmail))
{
/* privatenet / fakenet is not used */
if (m->from.point)
{
sprintf(text, "\01FMPT %d\r", m->from.point);
curr = InsertAfter(curr, text);
}
}
/* see if we want to zonegate and set the ZON flag if
necessary. The actual readressing proceudre is done later. */
if (m->from.zone != m->to.zone && !m->attrib.direct &&
!m->attrib.crash && CurArea.netmail &&
(SW->gate == GZONES || SW->gate == BOTH ||
m->attrib.zon ||
(SW->gate == GASK && askgate("zone"))
))
{
do_zonegate = 1;
m->attrib.zon = 1;
}
}
/* these babies go everywhere */
if (m->new)
{
if (SW->msgids)
{
sprintf(text, "\01MSGID: %s %08lx\r",
SW->domainmsgid ? show_address(&from) : show_4d(&from),
now);
now++;
curr = InsertAfter(curr, text);
}
}
else
{
if (m->msgid)
{
sprintf(text, "\01MSGID: %s\r", m->msgid);
curr = InsertAfter(curr, text);
}
}
if (m->has_timezone)
{
sprintf(text, "\01TZUTC: %.4li\r",
(long)(((m->timezone / 60) * 100) + (m->timezone % 60)));
curr = InsertAfter(curr, text);
}
if (m->reply && (!m->new || SW->msgids))
{
sprintf(text, "\01REPLY: %s\r", m->reply);
curr = InsertAfter(curr, text);
}
if ((SW->echoflags && CurArea.echomail) || (CurArea.netmail))
{
char flags[255];
printflags(flags, m, CurArea.msgtype, CurArea.echomail);
if (*flags)
{
sprintf(text, "\01FLAGS %s\r", flags);
curr = InsertAfter(curr, text);
}
}
if (!SW->usetearlines || SW->usepid || CurArea.netmail)
{
sprintf(text, "\01PID: %s %s\r", PROG, VERNUM VERPATCH);
curr = InsertAfter(curr, text);
}
if (CurArea.eightbits && write_chrs_kludge)
{
sprintf(text, "\01CHRS: %s 2\r", ST->output_charset);
curr = InsertAfter(curr, text);
if (ST->output_charset[0] != 'C' || ST->output_charset[1] != 'P')
{
int cp;
/* codepage of this kludge is non-obvious,
add CODEPAGE kludge */
cp = get_codepage_number(ST->output_charset);
if (cp != 0)
{
sprintf(text, "\01CODEPAGE: %d\r", cp);
curr = InsertAfter(curr, text);
}
}
}
/* domain gating */
if (domain_gated)
{
sprintf(text, "\01DOMAIN %s %d:%d/%d.%d %s %d:%d/%d.%d\r",
to.domain, to.zone, to.net, to.node, to.point, from.domain,
from.zone, from.net, from.node, from.point);
curr = InsertAfter(curr, text);
}
if (SW->soteot)
{
strcpy(text, "\01SOT:\r");
curr = InsertAfter(curr, text);
}
if (uucp_gated)
{
int cr = 0; /* we want to insert a \n after header info */
if (uucp_from)
{
sprintf(text, "From: %s\r", uucp_from);
curr = InsertAfter(curr, text);
ufrom = curr;
cr = 1;
}
if (uucp_to)
{
sprintf(text, "To: %s\r", uucp_to);
curr = InsertAfter(curr, text);
uto = curr;
cr = 1;
if (ST->uucpreplyto != NULL)
{
sprintf(text, "Reply-To: %s\r", ST->uucpreplyto);
curr = InsertAfter(curr, text);
ureplyto = curr;
}
}
if (cr == 1)
{
strcpy(text, "\r");
curr = InsertAfter(curr, text);
ublank = curr;
}
}
/*
* Actually assign the kludges we just created to the message body
* (before the text).
*/
if (curr != NULL)
{
curr->next = m->text;
if (m->text != NULL)
{
m->text->prev = curr;
}
while (curr->prev != NULL)
{
curr = curr->prev;
}
m->text = curr;
}
}
l = m->text;
while (l)
{
if (l->text == NULL)
{
l->text = xstrdup("\r");
}
if (*l->text)
{
s = strrchr(l->text, '\n');
if (s != NULL)
{
*s = '\r';
}
else
{
char *text;
text = xmalloc(strlen(l->text) + 5);
strcpy(text, l->text);
if (l->quote)
{
strcat(text, "\r");
}
else
{
if ((l->next == NULL || *(l->next->text) == '\0') &&
text[strlen(text) - 1] != '\r')
{
strcat(text, "\r");
}
else if (l->next != NULL && *(l->next->text) == '\n' &&
text[strlen(text) - 1] != '\r')
{
strcat(text, "\r");
}
else if (!m_isspace(*(text + strlen(text) - 1)))
{
if (*(text + strlen(text) - 1) == '.')
{
strcat(text, space_after_period(l));
}
else
{
strcat(text, " ");
}
}
}
release(l->text);
l->text = text;
}
}
l = l->next;
}
/* find the end of the message */
curr = m->text;
while (curr->next != NULL)
{
curr = curr->next;
}
if (!m->rawcopy)
{
if (SW->usetearlines && SW->useoriginlines &&
*curr->text != '\r' && CurArea.echomail)
{
strcpy(text, "\r");
curr = InsertAfter(curr, text);
xblank = curr;
}
if (SW->soteot)
{
strcpy(text, "\01EOT:\r");
curr = InsertAfter(curr, text);
}
if (CurArea.netmail && SW->netmailvia)
{
time_t td;
char time_str[80];
td = time(NULL);
strftime(time_str, 80, "%a %b %d %Y at %H:%M UTC", gmtime(&td));
sprintf(text, "\01Via " PROG " " VERNUM VERPATCH " %s, %s\r",
show_address(&from), time_str);
curr = InsertAfter(curr, text);
}
if (CurArea.echomail)
{
if (SW->usetearlines)
{
/* add the tearline */
if (got_tear) /* preserve user-defined tear line */
{
sprintf(text, "--- %s\r", tearline);
}
else
{
make_tearline(text);
strcat(text, "\r");
}
/* make sure it is not longer than 35 characters + \r*/
if (strlen(text) > tear_max + 1) /* +1 for \r */
{
text[tear_max + 1] = '\0';
text[tear_max] = '\r';
}
curr = InsertAfter(curr, text);
xtear = curr;
}
if (SW->useoriginlines)
{
/* add the origin line */
if (!got_origin)
{
GetOrigin(origin);
}
trans_token(origin, 79, m);
sprintf(text, " * Origin: %s (%s)\r", origin,
SW->domainorigin ? show_address(&from) : show_4d(&from));
/* make sure it is not longer than 79 characters + \r */
i = strlen(text) - 80;
if (i > 0)
{
s = strstr(text, " (");
*s = 0;
if (i > strlen(text)) /* assure data integrity */
{
i = strlen(text);
}
*s = ' ';
memmove (s - i, s, strlen(s) + 1);
}
curr = InsertAfter(curr, text);
xorigin = curr;
}
}
}
/* we've made the new message up, now return it's length */
l = m->text;
while (l)
{
if (*(l->text) != '\01')
{
ctrl = 0;
}
if (!ctrl)
{
temptext = translate_text(l->text, ltable);
if (temptext != NULL)
length += strlen(temptext);
release(temptext);
}
l = l->next;
}
length++; /* account for the \0 byte */
if (!m->rawcopy)
{
/* remap point originated crashmail */
/* if you change this if condition, don't forget to change the same
conditions some lines above in the generation of FMPT also! */
if (!m->attrib.direct && !m->attrib.crash && m->from.point &&
SW->pointnet != 0 && CurArea.netmail)
{
m->from.net = SW->pointnet;
m->from.node = m->from.point;
m->from.point = 0;
}
/* do any required zone gating */
if (do_zonegate)
{
m->to.node = m->to.zone;
m->to.zone = m->from.zone;
m->to.net = m->from.zone;
}
}
if (!m->rawcopy) /* recode the header, important for Russian users */
{
char *tmp;
tmp = translate_text(m->isfrom, ltable);
do_softcrxlat(tmp);
release(m->isfrom); m->isfrom = tmp;
tmp = translate_text(m->isto, ltable);
do_softcrxlat(tmp);
release(m->isto); m->isto = tmp;
tmp = translate_text(m->subj, ltable);
do_softcrxlat(tmp);
release(m->subj); m->subj = tmp;
}
abortWrite = 0;
while (MsgWriteHeader(m, WR_ALL) == ERR_OPEN_MSG && !abortWrite)
{
int ret;
ret = ChoiceBox(" Error! ", "Could not write message!",
"Retry", "Cancel", NULL);
if (ret == ID_TWO)
{
abortWrite = 1;
}
}
if (!abortWrite)
{
l = m->text;
while (l && !abortWrite)
{
if (l->text && *l->text != '\0')
{
if (!m->rawcopy)
{
temptext = translate_text(l->text, ltable);
do_softcrxlat(temptext);
/* output CHRS translation */
}
else
{
temptext = xstrdup(l->text);
}
while ((!abortWrite) &&
(MsgWriteText(temptext, n, length) == FALSE))
{
int ret;
ret = ChoiceBox(" Error! ", "Could not write message!",
"Retry", "Cancel", NULL);
if (ret == ID_TWO)
{
abortWrite = 1;
}
}
release(temptext);
}
/*
* The \r's have to be turned back into \n's because the
* message might be a CC: and so be needed again.
*/
s = strrchr(l->text, '\r');
if (s != NULL)
{
*s = '\n';
}
l = l->next;
}
MsgWriteText(NULL, n, length);
MsgClose();
}
CurArea.new = 1;
echotoss_add(&CurArea);
/*
* Clean up. If this message is a CC, then we don't want this
* temporary information to remain.
*/
if ((uucp_from || uucp_to) && (ublank != NULL))
{
deleteCrapLine(ublank);
}
if (uucp_from)
{
release(uucp_from);
deleteCrapLine(ufrom);
}
if (uucp_to)
{
release(uucp_to);
deleteCrapLine(uto);
if (ureplyto != NULL)
{
deleteCrapLine(ureplyto);
}
}
deleteCrapLine(xblank);
deleteCrapLine(xtear);
deleteCrapLine(xorigin);
#if 0
MsgUnlockArea();
#endif
if (abortWrite)
{
return FALSE;
}
else
{
return TRUE;
}
}
static void deleteCrapLine(LINE * crap)
{
if (crap != NULL)
{
if (crap->prev != NULL)
{
crap->prev->next = crap->next;
}
if (crap->next != NULL)
{
crap->next->prev = crap->prev;
}
release(crap->text);
release(crap);
}
}
#ifdef OS2
#include <os2.h>
static unsigned long setDefaultDisk(unsigned short x)
{
#ifdef OS216
return DosSelectDisk(x);
#else
return DosSetDefaultDisk(x);
#endif
}
static int changeDir(char *path)
{
#ifdef OS216
return chdir(path);
#else
return DosSetCurrentDir((PSZ) path);
#endif
}
void mygetcwd(char *buf, int len)
{
#ifdef OS216
getcwd(buf, len);
#else
unsigned long ulen;
ulen = len;
DosQueryCurrentDir(0, (PSZ) buf, &ulen);
#endif
}
#else
static int changeDir(char *path)
{
return chdir(path);
}
void mygetcwd(char *buf, int len)
{
#ifndef PACIFIC
getcwd(buf, len);
#else
getcwd(buf);
#endif
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1