This repository has been archived on 2024-04-08. You can view files and clone it, but cannot push or open issues or pull requests.
deb-mbse/mbsebbs/mail.c
2007-03-03 15:42:02 +00:00

2884 lines
70 KiB
C

/*****************************************************************************
*
* $Id$
* Purpose ...............: Message reading and writing.
* Todo ..................: Implement message groups.
*
*****************************************************************************
* Copyright (C) 1997-2007
*
* Michiel Broek FIDO: 2:280/2802
* Beekmansbos 10
* 1971 BV IJmuiden
* the Netherlands
*
* This file is part of MBSE BBS.
*
* This BBS 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.
*
* MBSE BBS 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 MBSE BBS; see the file COPYING. If not, write to the Free
* Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*****************************************************************************
*
* JAM(mbp) - Copyright 1993 Joaquim Homrighausen, Andrew Milner,
* Mats Birch, Mats Wallin.
* ALL RIGHTS RESERVED.
*
*****************************************************************************/
#include "../config.h"
#include "../lib/mbselib.h"
#include "../lib/mbse.h"
#include "../lib/users.h"
#include "../lib/nodelist.h"
#include "../lib/msgtext.h"
#include "../lib/msg.h"
#include "mail.h"
#include "funcs.h"
#include "input.h"
#include "language.h"
#include "misc.h"
#include "timeout.h"
#include "oneline.h"
#include "exitinfo.h"
#include "fsedit.h"
#include "filesub.h"
#include "msgutil.h"
#include "pop3.h"
#include "email.h"
#include "door.h"
#include "whoson.h"
#include "term.h"
#include "ttyio.h"
#include "openport.h"
/*
* Global variables
*/
unsigned int LastNum; /* Last read message number */
int Kludges = FALSE; /* Show kludges or not */
int Line = 1; /* Line counter in editor */
char *Message[TEXTBUFSIZE +1];/* Message compose text buffer */
FILE *qf; /* Quote file */
extern int do_mailout;
extern int LC_Wrote; /* Lastcaller info write message */
extern int rows;
/*
* Internal prototypes
*/
void ShowMsgHdr(void); /* Show message header */
int Read_a_Msg(unsigned int Num, int);/* Read a message */
int Export_a_Msg(unsigned int Num);/* Export message to homedir */
int ReadPanel(void); /* Read panel bar */
int Save_Msg(int, faddr *); /* Save a message */
int Save_CC(int, char *); /* Save carbon copy */
void Reply_Msg(int); /* Reply to message */
void Delete_MsgNum(unsigned int); /* Delete specified message */
int CheckUser(char *); /* Check if user exists */
int IsMe(char *); /* Test if this is my userrecord */
/****************************************************************************/
/*
* More prompt, returns 1 if user decides not to look any further.
*/
int LC(int Lines)
{
int z;
iLineCount += Lines;
if (iLineCount >= rows && iLineCount < 1000) {
iLineCount = 1;
pout(CFG.MoreF, CFG.MoreB, (char *) Language(61));
alarm_on();
z = toupper(Readkey());
if (z == Keystroke(61, 1)) {
Enter(1);
return(1);
}
if (z == Keystroke(61, 2))
iLineCount = 50000;
Blanker(strlen(Language(61)));
}
return(0);
}
/*
* Check if posting is allowed
*/
int Post_Allowed(void);
int Post_Allowed(void)
{
if (msgs.MsgKinds == RONLY) {
/* Message area is Readonly */
pout(LIGHTRED, BLACK, (char *) Language(437));
sleep(3);
return FALSE;
}
return TRUE;
}
/*
* Check if Alias is allowed, and if so if the user
* wants to use this in the From: field.
*/
int Alias_Option(void);
int Alias_Option(void)
{
int rc = TRUE;
if (!msgs.Aliases)
return FALSE;
if (strlen(exitinfo.sHandle) == 0)
return FALSE;
/* Use your alias ( */
pout(CYAN, BLACK, (char *)Language(477));
pout(CYAN, BLACK, exitinfo.sHandle);
/* YN|) to post this message [Y/n]: */
pout(CYAN, BLACK, (char *)Language(478));
colour(CFG.MsgInputColourF, CFG.MsgInputColourB);
alarm_on();
if (toupper(Readkey()) == Keystroke(478, 1)) {
rc = FALSE;
PUTCHAR(Keystroke(478, 1));
} else {
PUTCHAR(Keystroke(478, 0));
}
Enter(2);
return rc;
}
/*
* Check if netmail may be send crash or immediate.
*/
int Crash_Option(faddr *);
int Crash_Option(faddr *Dest)
{
node *Nlent;
int rc = 0;
unsigned short point;
if (exitinfo.Security.level < CFG.iCrashLevel)
return 0;
point = Dest->point;
Dest->point = 0;
if (((Nlent = getnlent(Dest)) != NULL) && (Nlent->addr.zone)) {
if ((Nlent->can_pots && Nlent->is_cm) || (Nlent->can_ip && Nlent->is_icm)) {
/* Crash [y/N]: */
pout(CYAN, BLACK, (char *)Language(461));
colour(CFG.MsgInputColourF, CFG.MsgInputColourB);
alarm_on();
if (toupper(Readkey()) == Keystroke(461, 0)) {
rc = 1;
PUTCHAR(Keystroke(461, 0));
} else
PUTCHAR(Keystroke(461, 1));
} else {
/* Warning: node is not CM, send Immediate [y/N]: */
pout(CYAN, BLACK, (char *)Language(462));
colour(CFG.MsgInputColourF, CFG.MsgInputColourB);
alarm_on();
if (toupper(Readkey()) == Keystroke(462, 0)) {
rc = 2;
PUTCHAR(Keystroke(462, 0));
} else
PUTCHAR(Keystroke(462, 1));
}
if (Nlent->addr.domain)
free(Nlent->addr.domain);
Nlent->addr.domain = NULL;
if (Nlent->url)
free(Nlent->url);
Nlent->url = NULL;
} else {
Syslog('+', "Node %s not found", ascfnode(Dest, 0x1f));
rc = -1;
}
Dest->point = point;
return rc;
}
/*
* Ask if message must be private, only allowed in areas which allow
* both public and private. Private areas are forced to private.
*/
int IsPrivate(void);
int IsPrivate(void)
{
int rc = FALSE;
if (msgs.MsgKinds == BOTH) {
Enter(1);
/* Private [y/N]: */
pout(CYAN, BLACK, (char *) Language(163));
alarm_on();
if (toupper(Readkey()) == Keystroke(163, 0)) {
rc = TRUE;
PUTCHAR(Keystroke(163, 0));
} else {
PUTCHAR(Keystroke(163, 1));
}
}
/*
* Allways set the private flag in Private areas.
*/
if (msgs.MsgKinds == PRIVATE)
rc = TRUE;
return rc;
}
void Check_Attach(void);
void Check_Attach(void)
{
char *Attach, *dospath, msg[81];
struct stat sb;
/*
* This is a dangerous option! Every file on the system to which the
* bbs has read access and is in the range of paths translatable by
* Unix to DOS can be attached to the netmail.
*/
if ((msgs.Type == NETMAIL) && (exitinfo.Security.level >= CFG.iAttachLevel)) {
Attach = calloc(PATH_MAX, sizeof(char));
while (TRUE) {
Enter(1);
/* Attach file [y/N]: */
pout(CYAN, BLACK, (char *)Language(463));
alarm_on();
if (toupper(Readkey()) == Keystroke(463, 0)) {
PUTCHAR(Keystroke(463, 0));
Enter(1);
/* Please enter filename: */
pout(YELLOW, BLACK, (char *)Language(245));
colour(CFG.MsgInputColourF, CFG.MsgInputColourB);
alarm_on();
snprintf(Attach, PATH_MAX, "%s/", CFG.uxpath);
PUTSTR(Attach);
GetstrP(Attach, 71, strlen(Attach));
if (strcmp(Attach, "") == 0)
break;
if ((stat(Attach, &sb) == 0) && (S_ISREG(sb.st_mode))) {
dospath = xstrcpy(Unix2Dos(Attach));
if (strncasecmp(Attach, CFG.uxpath, strlen(CFG.uxpath)) == 0) {
Syslog('+', "FileAttach \"%s\"", Attach);
if (strlen(CFG.dospath))
strcpy(Msg.Subject, dospath);
else
snprintf(Msg.Subject, 101, "%s", Attach);
Msg.FileAttach = TRUE;
Enter(1);
/* File */ /* will be attached */
snprintf(msg, 81, "%s %s %s", (char *)Language(464), Msg.Subject, Language(465));
pout(LIGHTCYAN, BLACK, msg);
Enter(1);
sleep(2);
break;
} else {
Enter(1);
/* File not within */
snprintf(msg, 81, "%s \"%s\"", Language(466), CFG.uxpath);
pout(LIGHTGREEN, BLACK, msg);
Enter(1);
Pause();
}
} else {
Enter(1);
/* File does not exist, please try again ... */
pout(LIGHTGREEN, BLACK, (char *)Language(296));
Enter(1);
Pause();
}
} else {
break;
} /* if attach */
} /* while true */
free(Attach);
}
}
/*
* Comment to sysop
*/
void SysopComment(char *Cmt)
{
unsigned int tmp;
char *temp;
FILE *fp;
tmp = iMsgAreaNumber;
/*
* Make sure that the .quote file is empty.
*/
temp = calloc(PATH_MAX, sizeof(char));
snprintf(temp, PATH_MAX, "%s/%s/.quote", CFG.bbs_usersdir, exitinfo.Name);
if ((fp = fopen(temp, "w")) != NULL)
fclose(fp);
free(temp);
SetMsgArea(CFG.iSysopArea -1);
snprintf(Msg.From, 101, "%s", CFG.sysop_name);
snprintf(Msg.Subject, 101, "%s", Cmt);
Reply_Msg(FALSE);
SetMsgArea(tmp);
}
/*
* Edit a message. Call the users preffered editor.
*/
int Edit_Msg()
{
switch (exitinfo.MsgEditor) {
case X_LINEEDIT: return Fs_Edit();
case FSEDIT: return Fs_Edit();
case EXTEDIT: return Ext_Edit();
}
return 0;
}
/*
* Post a message, called from the menu or ReadPanel().
*/
void Post_Msg()
{
int i, x, cc;
char *FidoNode, msg[81];
faddr *Dest = NULL;
node *Nlent = NULL;
unsigned short point;
Line = 1;
WhosDoingWhat(READ_POST, NULL);
SetMsgArea(iMsgAreaNumber);
clear();
if (!Post_Allowed())
return;
for (i = 0; i < (TEXTBUFSIZE + 1); i++)
Message[i] = (char *) calloc(MAX_LINE_LENGTH +1, sizeof(char));
Line = 1;
Msg_New();
Enter(1);
/* Posting message in area: */
snprintf(msg, 81, "%s\"%s\"", (char *) Language(156), sMsgAreaDesc);
pout(LIGHTBLUE, BLACK, msg);
Enter(1);
if (Alias_Option()) {
/*
* Set Handle
*/
strcpy(Msg.From, exitinfo.sHandle);
tlcap(Msg.From); // Do we want this???
/* From : */
pout(YELLOW, BLACK, (char *) Language(157));
} else {
/*
* Normal from address, no alias
*/
/* From : */
pout(YELLOW, BLACK, (char *) Language(157));
if (msgs.Type == NEWS) {
if (CFG.EmailMode != E_PRMISP) {
/*
* If no internet mail domain, use fidonet addressing.
*/
Dest = fido2faddr(CFG.EmailFidoAka);
strcpy(Msg.From, exitinfo.sUserName);
tlcap(Msg.From);
} else {
snprintf(Msg.From, 101, "%s@%s (%s)", exitinfo.Name, CFG.sysdomain, exitinfo.sUserName);
}
} else {
strcpy(Msg.From, exitinfo.sUserName);
tlcap(Msg.From);
}
}
pout(CFG.MsgInputColourF, CFG.MsgInputColourB, Msg.From);
Syslog('b', "Setting From: %s", Msg.From);
if ((msgs.Type == NEWS) || (msgs.Type == LIST)) {
/*
* Newsmode or maillist mode, automatic addressing to All.
* */
strcpy(Msg.To, "All");
} else {
while (TRUE) {
Enter(1);
/* To : */
pout(YELLOW, BLACK, (char *) Language(158));
colour(CFG.MsgInputColourF, CFG.MsgInputColourB);
Getname(Msg.To, 35);
if ((strcmp(Msg.To, "")) == 0) {
for(i = 0; i < (TEXTBUFSIZE + 1); i++)
free(Message[i]);
return;
}
if ((strcasecmp(Msg.To, "sysop")) == 0)
strcpy(Msg.To, CFG.sysop_name);
/*
* Localmail and Echomail may be addressed to All
*/
if ((msgs.Type == LOCALMAIL) || (msgs.Type == ECHOMAIL) || (msgs.Type == LIST)) {
if (strcasecmp(Msg.To, "all") == 0)
x = TRUE;
else {
/*
* Local users must exist in the userbase.
*/
if (msgs.Type == LOCALMAIL) {
/* Verifying user ... */
pout(CYAN, BLACK, (char *) Language(159));
x = CheckUser(Msg.To);
} else
x = TRUE;
}
} else if (msgs.Type == NETMAIL) {
x = FALSE;
Enter(1);
pout(YELLOW, BLACK, (char *)"Address : ");
FidoNode = calloc(61, sizeof(char));
colour(CFG.MsgInputColourF, CFG.MsgInputColourB);
GetstrC(FidoNode, 60);
if ((Dest = parsefnode(FidoNode)) != NULL) {
point = Dest->point;
Dest->point = 0;
if (((Nlent = getnlent(Dest)) != NULL) && (Nlent->addr.zone)) {
if (Nlent->url)
free(Nlent->url);
Nlent->url = NULL;
if (Nlent->addr.domain)
free(Nlent->addr.domain);
Nlent->addr.domain = NULL;
colour(YELLOW, BLACK);
if (point)
PUTSTR((char *)"Boss : ");
else
PUTSTR((char *)"Node : ");
Dest->point = point;
snprintf(msg, 81, "%s in %s", Nlent->name, Nlent->location);
pout(CFG.MsgInputColourF, CFG.MsgInputColourB, msg);
/* " Is this correct [y/N]: " */
pout(YELLOW, BLACK, (char *)Language(21));
colour(CFG.MsgInputColourF, CFG.MsgInputColourB);
alarm_on();
if (toupper(Readkey()) == Keystroke(21, 0)) {
Enter(1);
snprintf(Msg.ToAddress, 101, "%s", ascfnode(Dest, 0x1f));
x = TRUE;
switch (Crash_Option(Dest)) {
case 1: Msg.Crash = TRUE;
break;
case 2: Msg.Immediate = TRUE;
break;
}
}
} else {
Dest->point = point;
Enter(1);
/* Node not known, continue anayway [y/N]: */
pout(CYAN, BLACK, (char *) Language(241));
alarm_on();
if (toupper(Readkey()) == Keystroke(241, 0)) {
x = TRUE;
Syslog('+', "Node %s not found, forced continue", FidoNode);
}
}
} else {
Syslog('m', "Can't parse address %s", FidoNode);
}
free(FidoNode);
} else {
x = FALSE;
}
if(!x) {
PUTCHAR('\r');
/* User not found. Try again, or (Enter) to quit */
pout(CYAN, BLACK, (char *) Language(160));
} else
break;
}
}
Enter(1);
/* Subject : */
pout(YELLOW, BLACK, (char *) Language(161));
colour(CFG.MsgInputColourF, CFG.MsgInputColourB);
alarm_on();
GetstrP(Msg.Subject, 65, 0);
mbse_CleanSubject(Msg.Subject);
if ((strcmp(Msg.Subject, "")) == 0) {
Enter(1);
/* Abort Message [y/N] ?: */
pout(CYAN, BLACK, (char *) Language(162));
alarm_on();
if (toupper(Readkey()) == Keystroke(162, 0)) {
for(i = 0; i < (TEXTBUFSIZE + 1); i++)
free(Message[i]);
tidy_faddr(Dest);
return;
}
}
/*
* If not addressed to "all" and the area is Private/Public we
* ask the user for the private flag.
*/
if ((strcasecmp(Msg.To, "all")) != 0)
Msg.Private = IsPrivate();
Check_Attach();
if (Edit_Msg()) {
Enter(1);
if (msgs.Type == NETMAIL) {
/*
* Check for Carbon Copy lines, process them if present.
*/
cc = 0;
for (i = 1; i <= Line; i++) {
if (strncasecmp(Message[i], "cc: ", 4)) {
break;
} else {
cc++;
}
}
Syslog('b', "CC: detected %d", cc);
if (cc) {
/*
* Carbon copies, modify the text to show the presence of CCs.
*/
for (i = Line; i; i--) {
Syslog('b', "%02d: \"%s\"", i, printable(Message[i], 0));
snprintf(Message[i + 1], TEXTBUFSIZE +1, Message[i]);
}
Line++;
snprintf(Message[1], TEXTBUFSIZE +1, " +: Original message to %s", ascfnode(Dest, 0x4f));
for (i = 1; i <= Line; i++) {
Syslog('b', "%02d: \"%s\"", i, printable(Message[i], 0));
}
/*
* First sent to original destination
*/
Save_Msg(FALSE, Dest);
/*
* Now sent copies
*/
for (i = 0; i < cc; i++) {
Save_CC(FALSE, Message[i+2]);
}
} else {
Save_Msg(FALSE, Dest);
}
} else {
Save_Msg(FALSE, Dest);
}
Enter(1);
sleep(2);
}
for (i = 0; i < (TEXTBUFSIZE + 1); i++)
free(Message[i]);
tidy_faddr(Dest);
}
/*
* Save a Carbon Copy
* The ccline should have the format "cc: Firstname Lastname z:n/n.p"
*/
int Save_CC(int IsReply, char *ccline)
{
faddr *Dest = NULL;
int i, j, x, rc = FALSE;
char *p, *username, msg[81];
unsigned short point;
node *Nlent;
Syslog('b', "Save_CC(%s, %s)", IsReply ?"TRUE":"FALSE", ccline);
/*
* Reformat the line and extract username and node
*/
i = 4;
j = strlen(ccline);
while (ccline[i] == ' ')
i++;
while (ccline[j] != ' ')
j--;
Syslog('b', "i=%d, j=%d", i, j);
if (j <= i) {
Syslog('+', "Could not parse %s", printable(ccline, 0));
/* Could not parse */
snprintf(msg, 81, "%s \"%s\"", Language(22), printable(ccline, 0));
pout(LIGHTRED, BLACK, msg);
Enter(1);
Pause();
return FALSE;
}
username = calloc(j - i + 1, sizeof(char));
strncpy(username, ccline+i, j - i);
Syslog('b', "Username: \"%s\"", printable(username, 0));
while (*(p = username + strlen(username) -1) == ' ')
*p = '\0';
Syslog('b', "Username: \"%s\"", tlcap(printable(username, 0)));
if (strlen(username) == 0) {
Syslog('+', "Could not extract username from %s", printable(ccline, 0));
/* Could not parse */
snprintf(msg, 81, "%s \"%s\"", Language(22), printable(ccline, 0));
pout(LIGHTRED, BLACK, msg);
Enter(1);
Pause();
return FALSE;
}
if ((Dest = parsefnode(ccline + j)) == NULL) {
Syslog('+', "Could not extract address from %s", printable(ccline, 0));
/* Could not parse */
snprintf(msg, 81, "%s \"%s\"", Language(22), printable(ccline, 0));
pout(LIGHTRED, BLACK, msg);
Enter(1);
Pause();
return FALSE;
}
Dest->name = tlcap(printable(username, 0));
Syslog('b', "Dest %s", ascfnode(Dest, 0xff));
Enter(1);
snprintf(msg, 81, "Confirm CC to %s", ascfnode(Dest, 0xff));
pout(LIGHTMAGENTA, BLACK, msg);
Enter(1);
x = FALSE;
point = Dest->point;
Dest->point = 0;
if (((Nlent = getnlent(Dest)) != NULL) && (Nlent->addr.zone)) {
colour(YELLOW, BLACK);
if (point)
PUTSTR((char *)"Boss : ");
else
PUTSTR((char *)"Node : ");
Dest->point = point;
snprintf(msg, 81, "%s in %s", Nlent->name, Nlent->location);
pout(CFG.MsgInputColourF, CFG.MsgInputColourB, msg);
/* " Is this correct [y/N]: " */
pout(YELLOW, BLACK, (char *)Language(21));
colour(CFG.MsgInputColourF, CFG.MsgInputColourB);
alarm_on();
if (toupper(Readkey()) == Keystroke(21, 0)) {
Enter(1);
snprintf(Msg.ToAddress, 101, "%s", ascfnode(Dest, 0x1f));
x = TRUE;
switch (Crash_Option(Dest)) {
case 1: Msg.Crash = TRUE;
break;
case 2: Msg.Immediate = TRUE;
break;
}
}
} else {
Dest->point = point;
Enter(1);
/* Node not known, continue anayway [y/N]: */
pout(CYAN, BLACK, (char *) Language(241));
alarm_on();
if (toupper(Readkey()) == Keystroke(241, 0)) {
x = TRUE;
Syslog('+', "Node %s not found, forced continue", ascfnode(Dest, 0x0f));
}
}
if (x) {
Enter(1);
rc = Save_Msg(IsReply, Dest);
}
tidy_faddr(Dest);
free(username);
return rc;
}
/*
* Save the message to disk.
*/
int Save_Msg(int IsReply, faddr *Dest)
{
int i;
char *temp;
FILE *fp;
Syslog('b', "Entering Save_Msg() Line=%d, Dest=%s", Line, (Dest == NULL)?"NULL":"valid");
if (Line < 2)
return TRUE;
/* Saving message to disk */
pout(CFG.HiliteF, CFG.HiliteB, (char *) Language(202));
if (!Open_Msgbase(msgs.Base, 'w')) {
WriteError("Failed to open msgbase \"%s\"", msgs.Base);
return FALSE;
}
Msg.Written = Msg.Arrived = time(NULL) - (gmt_offset((time_t)0) * 60);
Msg.Local = TRUE;
temp = calloc(PATH_MAX, sizeof(char));
if (strlen(Msg.ReplyTo) && (msgs.Type == NETMAIL)) {
/*
* Send message to internet gateway.
*/
Syslog('m', "UUCP message to %s", Msg.ReplyAddr);
snprintf(Msg.To, 101, "UUCP");
Add_Headkludges(Dest, IsReply);
snprintf(temp, 101, "To: %s", Msg.ReplyAddr);
MsgText_Add2(temp);
MsgText_Add2((char *)"");
} else {
Add_Headkludges(Dest, IsReply);
}
/*
* Add message text
*/
for (i = 1; i <= Line; i++)
MsgText_Add2(Message[i]);
Add_Footkludges(TRUE, NULL, FALSE);
/*
* Save if to disk
*/
Msg_AddMsg();
Msg_UnLock();
snprintf(temp, 81, " (%d)", Msg.Id);
PUTSTR(temp);
Enter(1);
ReadExitinfo();
exitinfo.iPosted++;
WriteExitinfo();
LC_Wrote = TRUE;
Syslog('+', "Msg (%ld) to \"%s\", \"%s\", in %d", Msg.Id, Msg.To, Msg.Subject, iMsgAreaNumber + 1);
msgs.LastPosted = time(NULL);
msgs.Posted.total++;
msgs.Posted.tweek++;
msgs.Posted.tdow[Diw]++;
msgs.Posted.month[Miy]++;
snprintf(temp, PATH_MAX, "%s/etc/mareas.data", getenv("MBSE_ROOT"));
if ((fp = fopen(temp, "r+")) != NULL) {
fseek(fp, msgshdr.hdrsize + (iMsgAreaNumber * (msgshdr.recsize + msgshdr.syssize)), SEEK_SET);
fwrite(&msgs, msgshdr.recsize, 1, fp);
fclose(fp);
}
if (strlen(msgs.Group)) {
snprintf(temp, PATH_MAX, "%s/etc/mgroups.data", getenv("MBSE_ROOT"));
if ((fp = fopen(temp, "r+")) != NULL) {
fread(&mgrouphdr, sizeof(mgrouphdr), 1, fp);
while ((fread(&mgroup, mgrouphdr.recsize, 1, fp)) == 1) {
if (!strcmp(msgs.Group, mgroup.Name)) {
mgroup.LastDate = time(NULL);
mgroup.MsgsSent.total++;
mgroup.MsgsSent.tweek++;
mgroup.MsgsSent.tdow[l_date->tm_wday]++;
mgroup.MsgsSent.month[l_date->tm_mon]++;
fseek(fp, - mgrouphdr.recsize, SEEK_CUR);
fwrite(&mgroup, mgrouphdr.recsize, 1, fp);
break;
}
}
fclose(fp);
} else {
WriteError("$Save_Msg(): Can't open %s", temp);
}
}
/*
* Add quick mailscan info
*/
if (msgs.Type != LOCALMAIL) {
do_mailout = TRUE;
snprintf(temp, PATH_MAX, "%s/tmp/%smail.jam", getenv("MBSE_ROOT"),
((msgs.Type == ECHOMAIL) || (msgs.Type == LIST))? "echo" : "net");
if ((fp = fopen(temp, "a")) != NULL) {
fprintf(fp, "%s %u\n", msgs.Base, Msg.Id);
fclose(fp);
}
}
free(temp);
Close_Msgbase(msgs.Base);
SetMsgArea(iMsgAreaNumber);
return TRUE;
}
/*
* Show message header screen top for reading messages.
*/
void ShowMsgHdr(void)
{
static char Buf1[35], Buf2[35], Buf3[81];
char msg[81];
struct tm *tm;
time_t now;
int color;
Buf1[0] = '\0';
Buf2[0] = '\0';
Buf3[0] = '\0';
clear();
snprintf(msg, 81, " %-70s", sMsgAreaDesc);
pout(BLUE, LIGHTGRAY, msg);
snprintf(msg, 81,"#%-5u", Msg.Id);
pout(RED, LIGHTGRAY, msg);
Enter(1);
/* Date : */
pout(YELLOW, BLACK, (char *) Language(206));
colour(GREEN, BLACK);
/* Use intermediate variable to prevent SIGBUS on Sparc's */
now = Msg.Written;
tm = gmtime(&now);
snprintf(msg, 81, "%02d-%02d-%d %02d:%02d:%02d", tm->tm_mday, tm->tm_mon+1,
tm->tm_year+1900, tm->tm_hour, tm->tm_min, tm->tm_sec);
PUTSTR(msg);
colour(LIGHTRED, BLACK);
if (Msg.Local) PUTSTR((char *)" Local");
if (Msg.Intransit) PUTSTR((char *)" Transit");
if (Msg.Private) PUTSTR((char *)" Priv.");
if (Msg.Received) PUTSTR((char *)" Rcvd");
if (Msg.Sent) PUTSTR((char *)" Sent");
if (Msg.KillSent) PUTSTR((char *)" KillSent");
if (Msg.ArchiveSent) PUTSTR((char *)" ArchiveSent");
if (Msg.Hold) PUTSTR((char *)" Hold");
if (Msg.Crash) PUTSTR((char *)" Crash");
if (Msg.Immediate) PUTSTR((char *)" Imm.");
if (Msg.Direct) PUTSTR((char *)" Dir");
if (Msg.Gate) PUTSTR((char *)" Gate");
if (Msg.FileRequest) PUTSTR((char *)" Freq");
if (Msg.FileAttach) PUTSTR((char *)" File");
if (Msg.TruncFile) PUTSTR((char *)" TruncFile");
if (Msg.KillFile) PUTSTR((char *)" KillFile");
if (Msg.ReceiptRequest) PUTSTR((char *)" RRQ");
if (Msg.ConfirmRequest) PUTSTR((char *)" CRQ");
if (Msg.Orphan) PUTSTR((char *)" Orphan");
if (Msg.Encrypt) PUTSTR((char *)" Crypt");
if (Msg.Compressed) PUTSTR((char *)" Comp");
if (Msg.Escaped) PUTSTR((char *)" 7bit");
if (Msg.ForcePU) PUTSTR((char *)" FPU");
if (Msg.Localmail) PUTSTR((char *)" Localmail");
if (Msg.Netmail) PUTSTR((char *)" Netmail");
if (Msg.Echomail) PUTSTR((char *)" Echomail");
if (Msg.News) PUTSTR((char *)" News");
if (Msg.Email) PUTSTR((char *)" E-mail");
if (Msg.Nodisplay) PUTSTR((char *)" Nodisp");
if (Msg.Locked) PUTSTR((char *)" LCK");
if (Msg.Deleted) PUTSTR((char *)" Del");
Enter(1);
/* From : */
pout(YELLOW, BLACK, (char *) Language(209));
if (IsMe(Msg.From))
color = LIGHTGREEN;
else
color = GREEN;
colour(color++, BLACK);
PUTSTR(chartran(Msg.From));
if (iMsgAreaType != LOCALMAIL) {
snprintf(msg, 81, " (%s)", Msg.FromAddress);
pout(color, BLACK, msg);
}
Enter(1);
/* To : */
pout(YELLOW, BLACK, (char *) Language(208));
if (IsMe(Msg.To))
color = LIGHTGREEN;
else
color = GREEN;
colour(color++, BLACK);
PUTSTR(chartran(Msg.To));
if (iMsgAreaType == NETMAIL) {
snprintf(msg, 81, " (%s)", Msg.ToAddress);
pout(color, BLACK, msg);
}
Enter(1);
/* Subject : */
pout(YELLOW, BLACK, (char *) Language(210));
colour(GREEN, BLACK);
PUTSTR(chartran(Msg.Subject));
Enter(1);
colour(CFG.HiliteF, CFG.HiliteB);
colour(YELLOW, BLUE);
if (Msg.Reply)
snprintf(Buf1, 35, "\"+\" %s %u", (char *)Language(211), Msg.Reply);
if (Msg.Original)
snprintf(Buf2, 35, " \"-\" %s %u", (char *)Language(212), Msg.Original);
snprintf(Buf3, 35, "%s%s ", Buf1, Buf2);
snprintf(msg, 81, "%77s ", Buf3);
pout(YELLOW, BLUE, msg);
Enter(1);
}
/*
* Export a message to file in the users home directory or to the rules directory.
*/
int Export_a_Msg(unsigned int Num)
{
char *p, msg[81];
int ShowMsg = TRUE, z, homedir = TRUE;
LastNum = Num;
iLineCount = 7;
WhosDoingWhat(READ_POST, NULL);
/*
* The sysop has a choice to export to the rules directory.
*/
if ((exitinfo.Security.level >= CFG.sysop_access) && strlen(msgs.Tag)) {
while (TRUE) {
Enter(2);
/* Export to (H)ome or (R)ules directory: */
pout(WHITE, RED, (char *) Language(11));
colour(CFG.MsgInputColourF, CFG.MsgInputColourB);
alarm_on();
z = toupper(Readkey());
if (z == Keystroke(11, 0)) {
PUTCHAR(Keystroke(478, 0));
break;
}
if (z == Keystroke(11, 1)) {
PUTCHAR(Keystroke(478, 1));
homedir = FALSE;
break;
}
}
}
Syslog('+', "Export msg %d in area #%d (%s) to %s directory", Num, iMsgAreaNumber + 1, sMsgAreaDesc, homedir?"Home":"Rules");
/*
* The area data is already set, so we can do the next things
*/
if (MsgBase.Total == 0) {
Enter(1);
/* There are no messages in this area */
pout(WHITE, BLACK, (char *) Language(205));
Enter(2);
Syslog('+', "No messages in area");
sleep(3);
return FALSE;
}
if (!Msg_Open(sMsgAreaBase)) {
WriteError("Error open JAM base %s", sMsgAreaBase);
return FALSE;
}
if (!Msg_ReadHeader(Num)) {
Enter(1);
/* Message doesn't exist */
pout(WHITE, BLACK, (char *)Language(77));
Enter(2);
Msg_Close();
sleep(3);
return FALSE;
}
if (Msg.Private) {
ShowMsg = FALSE;
if ((strcasecmp(exitinfo.sUserName, Msg.From) == 0) || (strcasecmp(exitinfo.sUserName, Msg.To) == 0))
ShowMsg = TRUE;
if (exitinfo.Security.level >= msgs.SYSec.level)
ShowMsg = TRUE;
}
if (!ShowMsg) {
Enter(1);
/* Private message, not owner */
pout(WHITE, BLACK, (char *) Language(82));
Enter(2);
Msg_Close();
sleep(3);
return FALSE;
}
/*
* Export the message text to the file in the users home/wrk directory
* or to the rules directory.
* Create the filename as <areanum>_<msgnum>.msg if exported to the
* home directory, if exported to a rule directory the name is the area tag.
* The message is written in M$DOS <cr/lf> format.
*/
p = calloc(PATH_MAX, sizeof(char));
if (homedir)
snprintf(p, PATH_MAX, "%s/%s/wrk/%d_%u.msg", CFG.bbs_usersdir, exitinfo.Name, iMsgAreaNumber + 1, Num);
else
snprintf(p, PATH_MAX, "%s/%s", CFG.rulesdir, msgs.Tag);
if ((qf = fopen(p, "w")) != NULL) {
free(p);
p = NULL;
if (Msg_Read(Num, 80)) {
if ((p = (char *)MsgText_First()) != NULL) {
do {
if ((p[0] == '\001') || (!strncmp(p, "SEEN-BY:", 8)) || (!strncmp(p, "AREA:", 5))) {
if (Kludges && homedir) {
if (p[0] == '\001') {
p[0] = 'a';
fprintf(qf, "^%s\r\n", p);
} else {
fprintf(qf, "%s\r\n", p);
}
}
} else {
if (homedir || (strncmp(p, "--- ", 4) && strncmp(p, " * Origin", 9))) {
/*
* In rules directory, don't write tearline and Origin line.
*/
fprintf(qf, "%s\r\n", p);
}
}
} while ((p = (char *)MsgText_Next()) != NULL);
}
}
fclose(qf);
} else {
WriteError("$Can't open %s", p);
}
Msg_Close();
/*
* Report the result.
*/
Enter(2);
if (homedir) {
/* Message exported to your private directory as: */
pout(CFG.TextColourF, CFG.TextColourB, (char *) Language(46));
snprintf(msg, 81, "%d_%u.msg", iMsgAreaNumber + 1, Num);
pout(CFG.HiliteF, CFG.HiliteB, msg);
} else {
/* Message exported to rules directory as */
pout(CFG.TextColourF, CFG.TextColourB, (char *) Language(12));
pout(CFG.HiliteF, CFG.HiliteB, msgs.Tag);
}
Enter(2);
Pause();
return TRUE;
}
/*
* Read a message on screen. Update the lastread pointers,
* except while scanning and reading new mail at logon.
*/
int Read_a_Msg(unsigned int Num, int UpdateLR)
{
char *p = NULL, *fn, *charset = NULL, *charsin = NULL;
int ShowMsg = TRUE;
lastread LR;
LastNum = Num;
iLineCount = 7;
WhosDoingWhat(READ_POST, NULL);
/*
* The area data is already set, so we can do the next things
*/
if (MsgBase.Total == 0) {
Enter(1);
/* There are no messages in this area */
pout(WHITE, BLACK, (char *) Language(205));
Enter(2);
sleep(3);
return FALSE;
}
if (!Msg_Open(sMsgAreaBase)) {
WriteError("Error open JAM base %s", sMsgAreaBase);
return FALSE;
}
if (!Msg_ReadHeader(Num)) {
Enter(1);
pout(WHITE, BLACK, (char *)Language(77));
Enter(2);
Msg_Close();
sleep(3);
return FALSE;
}
if (Msg.Private) {
ShowMsg = FALSE;
if ((strcasecmp(exitinfo.sUserName, Msg.From) == 0) || (strcasecmp(exitinfo.sUserName, Msg.To) == 0))
ShowMsg = TRUE;
if (exitinfo.Security.level >= msgs.SYSec.level)
ShowMsg = TRUE;
}
if (!ShowMsg) {
Enter(1);
pout(WHITE, BLACK, (char *) Language(82));
Enter(2);
Msg_Close();
sleep(3);
return FALSE;
}
/*
* Analyse this message to find the character encoding.
*/
if (Msg_Read(Num, 78)) {
if ((p = (char *)MsgText_First()) != NULL) {
do {
if ((p[0] == '\001') || (!strncmp(p, "SEEN-BY:", 8)) || (!strncmp(p, "AREA:", 5))) {
if (strncmp(p, "\001CHRS: ", 7) == 0) {
charset = xstrcpy(p + 7);
break;
}
if (strncmp(p, "\001CHARSET: ", 10) == 0) {
charset = xstrcpy(p + 10);
break;
}
}
} while ((p = (char *)MsgText_Next()) != NULL);
}
}
if (charset == NULL) {
/*
* No charset marked in the message, we are going to asume
* that an ancient dos program is used with IBMPC characters.
* This means CP437.
*/
charsin = xstrcpy((char *)"CP437");
} else {
charsin = xstrcpy(get_ic_ftn(find_ftn_charset(charset)));
}
chartran_init(charsin, get_ic_ftn(exitinfo.Charset), 'b');
/*
* Fill Quote file in case the user wants to reply. Note that line
* wrapping is set lower then normal message read, to create room
* for the Quote> strings at the start of each line. The text is
* converted to the users local characterset so the reply can be
* done with the users prefered characterset.
*/
fn = calloc(PATH_MAX, sizeof(char));
snprintf(fn, PATH_MAX, "%s/%s/.quote", CFG.bbs_usersdir, exitinfo.Name);
if ((qf = fopen(fn, "w")) != NULL) {
if (Msg_Read(Num, 75)) {
if ((p = (char *)MsgText_First()) != NULL) {
do {
if ((p[0] == '\001') || (!strncmp(p, "SEEN-BY:", 8)) || (!strncmp(p, "AREA:", 5))) {
if (Kludges) {
if (p[0] == '\001') {
p[0] = 'a';
fprintf(qf, "^%s\n", chartran(p));
} else {
fprintf(qf, "%s\n", chartran(p));
}
}
} else {
fprintf(qf, "%s\n", chartran(p));
}
} while ((p = (char *)MsgText_Next()) != NULL);
}
}
fclose(qf);
} else {
WriteError("$Can't open %s", p);
}
free(fn);
/*
* Show message header with charset mapping if needed.
*/
ShowMsgHdr();
/*
* Show message text
*/
colour(CFG.TextColourF, CFG.TextColourB);
if (Msg_Read(Num, 79)) {
if ((p = (char *)MsgText_First()) != NULL) {
do {
if ((p[0] == '\001') || (!strncmp(p, "SEEN-BY:", 8)) || (!strncmp(p, "AREA:", 5))) {
if (Kludges) {
colour(LIGHTGRAY, BLACK);
if (p[0] == '\001')
p[0]= 'a'; // Make it printable
PUTSTR(chartran(p));
Enter(1);
if (CheckLine(CFG.TextColourF, CFG.TextColourB, FALSE))
break;
}
} else {
colour(CFG.TextColourF, CFG.TextColourB);
if (strchr(p, '>') != NULL)
if ((strlen(p) - strlen(strchr(p, '>'))) < 10)
colour(CFG.HiliteF, CFG.HiliteB);
PUTSTR(chartran(p));
Enter(1);
if (CheckLine(CFG.TextColourF, CFG.TextColourB, FALSE))
break;
}
} while ((p = (char *)MsgText_Next()) != NULL);
}
}
if (charset)
free(charset);
if (charsin)
free(charsin);
chartran_close();
/*
* Set the Received status on this message if it's for the user.
*/
if ((!Msg.Received) && (strlen(Msg.To) > 0) &&
((strcasecmp(Msg.To, exitinfo.sUserName) == 0) || (strcasecmp(exitinfo.sHandle, Msg.To) == 0))) {
Msg.Received = TRUE;
Msg.Read = time(NULL) - (gmt_offset((time_t)0) * 60);
if (Msg_Lock(30L)) {
Msg_WriteHeader(Num);
Msg_UnLock();
}
}
/*
* Update lastread pointer if needed. Netmail boards are always updated.
*/
if (Msg_Lock(30L) && (UpdateLR || msgs.Type == NETMAIL)) {
LR.UserID = grecno;
p = xstrcpy(exitinfo.sUserName);
if (Msg_GetLastRead(&LR) == TRUE) {
LR.LastReadMsg = Num;
if (Num > LR.HighReadMsg)
LR.HighReadMsg = Num;
if (LR.HighReadMsg > MsgBase.Highest)
LR.HighReadMsg = MsgBase.Highest;
LR.UserCRC = StringCRC32(tl(p));
if (!Msg_SetLastRead(LR))
WriteError("Error update lastread");
} else {
/*
* Append new lastread pointer
*/
LR.UserCRC = StringCRC32(tl(p));
LR.UserID = grecno;
LR.LastReadMsg = Num;
LR.HighReadMsg = Num;
if (!Msg_NewLastRead(LR))
WriteError("Can't append lastread");
}
free(p);
Msg_UnLock();
}
Msg_Close();
return TRUE;
}
/*
* Read Messages, called from menu
*/
void Read_Msgs()
{
char *temp;
unsigned int Start;
lastread LR;
temp = calloc(81, sizeof(char));
Enter(1);
/* Message area \"%s\" contains %lu messages. */
snprintf(temp, 81, "%s\"%s\" %s%u %s", (char *) Language(221), sMsgAreaDesc,
(char *) Language(222), MsgBase.Total, (char *) Language(223));
pout(CFG.TextColourF, CFG.TextColourB, temp);
/*
* Check for lastread pointer, suggest lastread number for start.
*/
Start = MsgBase.Lowest;
if (Msg_Open(sMsgAreaBase)) {
LR.UserID = grecno;
if (Msg_GetLastRead(&LR))
Start = LR.HighReadMsg + 1;
else
Start = 1;
Msg_Close();
/*
* If we already have read the last message, the pointer is
* higher then HighMsgNum, we set it at HighMsgNum to prevent
* errors and read that message again.
*/
if (Start > MsgBase.Highest)
Start = MsgBase.Highest;
}
Enter(1);
/* Please enter a message between */
snprintf(temp, 81, "%s(%u - %u)", (char *) Language(224), MsgBase.Lowest, MsgBase.Highest);
pout(WHITE, BLACK, temp);
Enter(1);
/* Message number [ */
snprintf(temp, 81, "%s%u]: ", (char *) Language(225), Start);
PUTSTR(temp);
colour(CFG.InputColourF, CFG.InputColourB);
GetstrC(temp, 80);
if ((strcmp(temp, "")) != 0)
Start = atoi(temp);
free(temp);
if (!Read_a_Msg(Start, TRUE))
return;
if (MsgBase.Total == 0)
return;
while(ReadPanel()) {}
}
/*
* The panel bar under the messages while reading
*/
int ReadPanel()
{
int input;
WhosDoingWhat(READ_POST, NULL);
/*
* The writer of the message, sysops, and who has sysop rights to the message area
* are allowed to delete messages.
*/
if ((msgs.UsrDelete && IsMe(Msg.From)) || (exitinfo.Security.level >= CFG.sysop_access) ||
Access(exitinfo.Security, msgs.SYSec)) {
/* (A)gain, (N)ext, (L)ast, (R)eply, (E)nter, (D)elete, (Q)uit, e(X)port */
pout(WHITE, RED, (char *) Language(214));
} else {
/* (A)gain, (N)ext, (L)ast, (R)eply, (E)nter, (Q)uit, e(X)port */
pout(WHITE, RED, (char *) Language(215));
}
if ((exitinfo.Security.level >= CFG.sysop_access) || Access(exitinfo.Security, msgs.SYSec))
PUTSTR((char *)", (!)");
PUTSTR((char *)": ");
alarm_on();
input = toupper(Readkey());
if (input == '!') { /* ! Toggle kludges display */
if ((exitinfo.Security.level >= CFG.sysop_access) || Access(exitinfo.Security, msgs.SYSec)) {
if (Kludges)
Kludges = FALSE;
else
Kludges = TRUE;
}
Read_a_Msg(LastNum, TRUE);
} else if (input == Keystroke(214, 0)) { /* (A)gain */
Read_a_Msg(LastNum, TRUE);
} else if (input == Keystroke(214, 4)) { /* (P)ost */
Post_Msg();
Read_a_Msg(LastNum, TRUE);
} else if (input == Keystroke(214, 2)) { /* (L)ast */
if (LastNum > MsgBase.Lowest)
LastNum--;
Read_a_Msg(LastNum, TRUE);
} else if (input == Keystroke(214, 3)) { /* (R)eply */
Reply_Msg(TRUE);
Read_a_Msg(LastNum, TRUE);
} else if (input == Keystroke(214, 5)) { /* (Q)uit */
/* Quit */
pout(WHITE, BLACK, (char *) Language(189));
Enter(1);
return FALSE;
} else if (input == Keystroke(214, 7)) { /* e(X)port */
Export_a_Msg(LastNum);
Read_a_Msg(LastNum, TRUE);
} else if (input == '+') {
if (Msg.Reply)
LastNum = Msg.Reply;
Read_a_Msg(LastNum, TRUE);
} else if (input == '-') {
if (Msg.Original)
LastNum = Msg.Original;
Read_a_Msg(LastNum, TRUE);
} else if (input == Keystroke(214, 6)) { /* (D)elete */
if ((msgs.UsrDelete && IsMe(Msg.From)) || (exitinfo.Security.level >= CFG.sysop_access) ||
Access(exitinfo.Security, msgs.SYSec)) {
Delete_MsgNum(LastNum);
if (LastNum < MsgBase.Highest) {
LastNum++;
Read_a_Msg(LastNum, TRUE);
} else {
return FALSE;
}
} else {
Read_a_Msg(LastNum, TRUE);
}
} else {
/* Next */
pout(WHITE, BLACK, (char *) Language(216));
if (LastNum < MsgBase.Highest)
LastNum++;
else
return FALSE;
Read_a_Msg(LastNum, TRUE);
}
return TRUE;
}
/*
* Reply message, in Msg.From and Msg.Subject must be the
* name to reply to and the subject. IsReply is true if the
* message is a real reply, and false for forced messages such
* as "message to sysop"
*/
void Reply_Msg(int IsReply)
{
int i, j, x, cc;
char to[101], from[101], subj[101], msgid[101], replyto[101], replyaddr[101], *tmp, *buf, qin[6], msg[81];
faddr *Dest = NULL;
if (!Post_Allowed())
return;
strncpy(from, Msg.To, 100);
strncpy(to, Msg.From, 100);
strncpy(replyto, Msg.ReplyTo, 80);
/*
* For some reason there are sometimes spaces at the
* beginning of the reply address.
*/
tmp = Msg.ReplyAddr;
while (*tmp && isspace(*tmp))
tmp++;
strncpy(replyaddr, tmp, 100);
Dest = parsefnode(Msg.FromAddress);
Syslog('m', "Parsed from address %s", ascfnode(Dest, 0x1f));
if (strncasecmp(Msg.Subject, "Re:", 3) && strncasecmp(Msg.Subject, "Re^2:", 5) && IsReply) {
snprintf(subj, 101, "Re: ");
strncpy(subj+4, Msg.Subject, 97);
} else {
strncpy(subj, Msg.Subject, 101);
}
Syslog('m', "Reply msg to %s, subject %s", to, subj);
Syslog('m', "Msgid was %s", Msg.Msgid);
strncpy(msgid, Msg.Msgid, 100);
x = 0;
WhosDoingWhat(READ_POST, NULL);
clear();
snprintf(msg, 81, " %-71s", sMsgAreaDesc);
pout(BLUE, LIGHTGRAY, msg);
snprintf(msg, 81, "#%-5u", MsgBase.Highest + 1);
pout(RED, LIGHTGRAY, msg);
Enter(1);
colour(CFG.HiliteF, CFG.HiliteB);
if (utf8)
chartran_init((char *)"CP437", (char *)"UTF-8", 'B');
PUTSTR(chartran(sLine_str()));
chartran_close();
for (i = 0; i < (TEXTBUFSIZE + 1); i++)
Message[i] = (char *) calloc(MAX_LINE_LENGTH +1, sizeof(char));
Msg_New();
strncpy(Msg.Replyid, msgid, 101);
strncpy(Msg.ReplyTo, replyto, 101);
strncpy(Msg.ReplyAddr, replyaddr, 101);
/* From : */
if (Alias_Option()) {
/*
* Set handle
*/
strcpy(Msg.From, exitinfo.sHandle);
tlcap(Msg.From); // Do we want this???
} else {
if (msgs.Type == NEWS) {
if (CFG.EmailMode != E_PRMISP) {
/*
* If no inernet mail domain, use fidonet addressing
*/
strcpy(Msg.From, exitinfo.sUserName);
tlcap(Msg.From);
} else {
snprintf(Msg.From, 101, "%s@%s (%s)", exitinfo.Name, CFG.sysdomain, exitinfo.sUserName);
}
} else {
strncpy(Msg.From, exitinfo.sUserName, 101);
tlcap(Msg.From);
}
}
pout(YELLOW, BLACK, (char *) Language(209));
pout(CFG.MsgInputColourF, CFG.MsgInputColourB, Msg.From);
Enter(1);
/* To : */
strncpy(Msg.To, to, 101);
pout(YELLOW, BLACK, (char *) Language(208));
pout(CFG.MsgInputColourF, CFG.MsgInputColourB, Msg.To);
Enter(1);
/* Enter to keep Subject. */
pout(LIGHTRED, BLACK, (char *) Language(219));
Enter(1);
/* Subject : */
pout(YELLOW, BLACK, (char *) Language(210));
strncpy(Msg.Subject, subj, 101);
pout(CFG.MsgInputColourF, CFG.MsgInputColourB, Msg.Subject);
x = strlen(subj);
colour(CFG.MsgInputColourF, CFG.MsgInputColourB);
GetstrP(subj, 50, x);
mbse_CleanSubject(subj);
if (strlen(subj))
strcpy(Msg.Subject, subj);
Msg.Private = IsPrivate();
Enter(1);
/*
* If netmail reply and enough security level, allow crashmail.
*/
if (msgs.Type == NETMAIL) {
switch (Crash_Option(Dest)) {
case 1: Msg.Crash = TRUE;
break;
case 2: Msg.Immediate = TRUE;
break;
case -1:printf("\r");
/* Node not known, continue anayway [y/N]: */
pout(CYAN, BLACK, (char *) Language(241));
alarm_on();
if (toupper(Readkey()) == Keystroke(241, 0)) {
Syslog('+', "Node not found, forced continue");
} else {
for (i = 0; i < (TEXTBUFSIZE + 1); i++)
free(Message[i]);
return;
}
break;
}
}
Check_Attach();
/*
* Quote original message now, format the original users
* initials into qin. No quoting if this is a message to Sysop.
*/
Line = 1;
if (IsReply) {
snprintf(Message[1], TEXTBUFSIZE +1, "%s wrote to %s:", to, from);
memset(&qin, 0, sizeof(qin));
x = TRUE;
j = 0;
for (i = 0; i < strlen(to); i++) {
if (x && isalpha(to[i])) {
qin[j] = to[i];
j++;
x = FALSE;
}
if (to[i] == ' ' || to[i] == '.')
x = TRUE;
if (j == 6)
break;
}
Line = 2;
tmp = calloc(PATH_MAX, sizeof(char));
buf = calloc(TEXTBUFSIZE +1, sizeof(char));
snprintf(tmp, PATH_MAX, "%s/%s/.quote", CFG.bbs_usersdir, exitinfo.Name);
if ((qf = fopen(tmp, "r")) != NULL) {
while ((fgets(buf, TEXTBUFSIZE, qf)) != NULL) {
Striplf(buf);
snprintf(Message[Line], TEXTBUFSIZE +1, "%s> %s", (char *)qin, buf);
Line++;
if (Line == TEXTBUFSIZE)
break;
}
fclose(qf);
} else
WriteError("$Can't read %s", tmp);
free(buf);
free(tmp);
}
if (Edit_Msg()) {
Enter(1);
if (msgs.Type == NETMAIL) {
/*
* Check for Carbon Copy lines, process them if present.
*/
cc = 0;
for (i = 1; i <= Line; i++) {
if (strncasecmp(Message[i], "cc: ", 4)) {
break;
} else {
cc++;
}
}
Syslog('b', "CC: detected %d", cc);
if (cc) {
/*
* Carbon copies, modify the text to show the presence of CCs.
*/
for (i = Line; i; i--) {
Syslog('b', "%02d: \"%s\"", i, printable(Message[i], 0));
snprintf(Message[i + 1], TEXTBUFSIZE +1, Message[i]);
}
Line++;
snprintf(Message[1], TEXTBUFSIZE +1, " +: Original message to %s", ascfnode(Dest, 0x4f));
for (i = 1; i <= Line; i++) {
Syslog('b', "%02d: \"%s\"", i, printable(Message[i], 0));
}
/*
* First sent to original destination
*/
Save_Msg(IsReply, Dest);
/*
* Now sent copies
*/
for (i = 0; i < cc; i++) {
Save_CC(IsReply, Message[i+2]);
}
} else {
Save_Msg(IsReply, Dest);
}
} else {
Save_Msg(IsReply, Dest);
}
Enter(1);
sleep(3);
}
for (i = 0; i < (TEXTBUFSIZE + 1); i++)
free(Message[i]);
}
int IsMe(char *Name)
{
char *p, *q;
int i, rc = FALSE;
if (strlen(Name) == 0)
return FALSE;
if (strcasecmp(Name, exitinfo.sUserName) == 0)
rc = TRUE;
if (strcasecmp(Name, exitinfo.sHandle) == 0)
rc = TRUE;
q = xstrcpy(Name);
if (strstr(q, (char *)"@")) {
p = strtok(q, "@");
for (i = 0; i < strlen(p); i++)
if (p[i] == '_')
p[i] = ' ';
if (strcasecmp(p, exitinfo.sUserName) == 0)
rc = TRUE;
if (strcasecmp(p, exitinfo.sHandle) == 0)
rc = TRUE;
if (strcasecmp(p, exitinfo.Name) == 0)
rc = TRUE;
}
free(q);
return rc ;
}
void QuickScan_Msgs()
{
int FoundMsg = FALSE;
int i;
char msg[81];
iLineCount = 2;
WhosDoingWhat(READ_POST, NULL);
if (MsgBase.Total == 0) {
Enter(1);
/* There are no messages in this area. */
pout(WHITE, BLACK, (char *) Language(205));
Enter(3);
sleep(3);
return;
}
clear();
/* # From To Subject */
poutCR(YELLOW, BLUE, (char *) Language(220));
if (Msg_Open(sMsgAreaBase)) {
for (i = MsgBase.Lowest; i <= MsgBase.Highest; i++) {
if (Msg_ReadHeader(i) && ((msgs.Type != NETMAIL) ||
((msgs.Type == NETMAIL) && ((IsMe(Msg.From)) || (IsMe(Msg.To)))))) {
snprintf(msg, 81, "%-6u", Msg.Id);
pout(WHITE, BLACK, msg);
snprintf(msg, 81, "%s ", padleft(Msg.From, 20, ' '));
if (IsMe(Msg.From))
pout(LIGHTCYAN, BLACK, msg);
else
pout(CYAN, BLACK, msg);
snprintf(msg, 81, "%s ", padleft(Msg.To, 20, ' '));
if (IsMe(Msg.To))
pout(LIGHTGREEN, BLACK, msg);
else
pout(GREEN, BLACK, msg);
snprintf(msg, 81, "%s", padleft(Msg.Subject, 31, ' '));
pout(MAGENTA, BLACK, msg);
Enter(1);
FoundMsg = TRUE;
if (LC(1))
break;
}
}
Msg_Close();
}
if (!FoundMsg) {
Enter(1);
/* There are no messages in this area. */
pout(LIGHTGREEN, BLACK, (char *) Language(205));
Enter(2);
sleep(3);
}
iLineCount = 2;
Pause();
}
/*
* Called from the menu
*/
void Delete_Msg()
{
char *temp;
unsigned int Msgnum = 0L;
WhosDoingWhat(READ_POST, NULL);
/*
* The area data is already set, so we can do the next things
*/
if (MsgBase.Total == 0) {
Enter(1);
/* There are no messages in this area */
pout(WHITE, BLACK, (char *) Language(205));
Enter(2);
sleep(3);
return;
}
temp = calloc(81, sizeof(char));
Enter(1);
/* Message area \"%s\" contains %lu messages. */
snprintf(temp, 81, "%s\"%s\" %s%u %s", (char *) Language(221), sMsgAreaDesc,
(char *) Language(222), MsgBase.Total, (char *) Language(223));
pout(CFG.TextColourF, CFG.TextColourB, temp);
Enter(1);
/* Please enter a message between */
snprintf(temp, 81, "%s(%u - %u): ", (char *) Language(224), MsgBase.Lowest, MsgBase.Highest);
pout(WHITE, BLACK, temp);
colour(CFG.InputColourF, CFG.InputColourB);
GetstrC(temp, 10);
if ((strcmp(temp, "")) != 0)
Msgnum = atoi(temp);
free(temp);
if (!Msg_Open(sMsgAreaBase)) {
WriteError("Error open JAM base %s", sMsgAreaBase);
return;
}
if (!Msg_ReadHeader(Msgnum)) {
Enter(1);
/* Message doesn't exist */
pout(WHITE, BLACK, (char *)Language(77));
Enter(2);
Msg_Close();
sleep(3);
return;
}
Msg_Close();
/*
* Message does exist and a valid number is suplied, check and finally mark the message deleted.
*/
if ((msgs.UsrDelete && IsMe(Msg.From)) || (exitinfo.Security.level >= CFG.sysop_access) ||
Access(exitinfo.Security, msgs.SYSec)) {
Delete_MsgNum(Msgnum);
} else {
Enter(1);
pout(LIGHTRED, BLACK, (char *)Language(14));
Enter(2);
}
sleep(3);
return;
}
/*
* Check linecounter for reading messages.
*/
int CheckLine(int FG, int BG, int Email)
{
int x, z;
x = strlen(Language(61));
iLineCount++;
if ((iLineCount >= (rows -1)) && (iLineCount < 1000)) {
iLineCount = 7;
DoNop();
pout(CFG.MoreF, CFG.MoreB, (char *) Language(61));
alarm_on();
z = tolower(Readkey());
Blanker(x);
switch(z) {
case 'n': Enter(1);
return TRUE;
break;
case '=': iLineCount = 1000;
}
if (Email)
ShowEmailHdr();
else
ShowMsgHdr();
colour(FG, BG);
}
return FALSE;
}
/*
* Select message area from the list.
*/
void MsgArea_List(char *Option)
{
FILE *pAreas;
int iAreaCount = 6, Recno = 0, iOldArea = 0, iAreaNum = 0, loopcount = 0;
int iGotArea = FALSE; /* Flag to check if user typed in area */
int iCheckNew = FALSE; /* Flag to check for new mail in area */
int offset;
char *temp, msg[81];
lastread LR;
temp = calloc(PATH_MAX, sizeof(char));
snprintf(temp, PATH_MAX, "%s/etc/mareas.data", getenv("MBSE_ROOT"));
/*
* Save old area, incase he picks a invalid area
*/
iOldArea = iMsgAreaNumber;
if ((pAreas = fopen(temp, "rb")) == NULL) {
WriteError("$Can't open %s", temp);
free(temp);
return;
}
/*
* Count how many records there are
*/
fread(&msgshdr, sizeof(msgshdr), 1, pAreas);
fseek(pAreas, 0, SEEK_END);
iAreaNum = (ftell(pAreas) - msgshdr.hdrsize) / (msgshdr.recsize + msgshdr.syssize);
/*
* If there are menu options, parse them first
* 1. Check for New Messages in Area (only option that will continue to display list)
* 2. Increment Area, return
* 3. Decrement Area, return
* 4. Select area direct (via area number), return
*/
if (strlen(Option) != 0) {
if (strcmp(Option, "N") == 0) {
iCheckNew = TRUE;
} else {
if (strcmp(Option, "U+") == 0) {
while(TRUE) {
iMsgAreaNumber++;
if (iMsgAreaNumber >= iAreaNum) {
iMsgAreaNumber = 0;
loopcount++;
if (loopcount > 1) {
/* No more areas with unread messages */
pout(LIGHTRED, BLACK, (char *) Language(479));
iMsgAreaNumber = iOldArea;
Enter(2);
Pause();
free(temp);
fclose(pAreas);
return;
}
}
offset = msgshdr.hdrsize + (iMsgAreaNumber * (msgshdr.recsize + msgshdr.syssize));
if (fseek(pAreas, offset, 0) != 0) {
WriteError("$Can't move pointer in %s", temp);
}
fread(&msgs, msgshdr.recsize, 1, pAreas);
if ((Access(exitinfo.Security, msgs.RDSec)) && (msgs.Active) && (strlen(msgs.Password) == 0)) {
if (Msg_Open(msgs.Base)) {
MsgBase.Highest = Msg_Highest();
LR.UserID = grecno;
if (Msg_GetLastRead(&LR) != TRUE) {
LR.HighReadMsg = 0;
}
if (MsgBase.Highest > LR.HighReadMsg) {
Msg_Close();
break;
}
Msg_Close();
}
}
}
}
if (strcmp(Option, "U-") == 0) {
while(TRUE) {
iMsgAreaNumber--;
if (iMsgAreaNumber < 0) {
iMsgAreaNumber = iAreaNum - 1;
loopcount++;
if (loopcount > 1) {
/* No more areas with unread messages */
pout(LIGHTRED, BLACK, (char *) Language(479));
iMsgAreaNumber = iOldArea;
Enter(2);
Pause();
fclose(pAreas);
free(temp);
return;
}
}
offset = msgshdr.hdrsize + (iMsgAreaNumber * (msgshdr.recsize + msgshdr.syssize));
if (fseek(pAreas, offset, 0) != 0) {
WriteError("$Can't move pointer in %s", temp);
}
fread(&msgs, msgshdr.recsize, 1, pAreas);
if ((Access(exitinfo.Security, msgs.RDSec)) && (msgs.Active) && (strlen(msgs.Password) == 0)) {
if (Msg_Open(msgs.Base)) {
MsgBase.Highest = Msg_Highest();
LR.UserID = grecno;
if (Msg_GetLastRead(&LR) != TRUE ){
LR.HighReadMsg = 0;
}
if (MsgBase.Highest > LR.HighReadMsg) {
Msg_Close();
break;
}
Msg_Close();
}
}
}
}
if (strcmp(Option, "M+") == 0) {
while (TRUE) {
iMsgAreaNumber++;
if (iMsgAreaNumber >= iAreaNum) {
iMsgAreaNumber = 0;
}
offset = msgshdr.hdrsize + (iMsgAreaNumber * (msgshdr.recsize + msgshdr.syssize));
if(fseek(pAreas, offset, 0) != 0) {
WriteError("$Can't move pointer in %s", temp);
}
fread(&msgs, msgshdr.recsize, 1, pAreas);
if ((Access(exitinfo.Security, msgs.RDSec)) && (msgs.Active) && (strlen(msgs.Password) == 0)) {
break;
}
}
}
if (strcmp(Option, "M-") == 0) {
while (TRUE) {
iMsgAreaNumber--;
if (iMsgAreaNumber < 0) {
iMsgAreaNumber = iAreaNum -1;
}
offset = msgshdr.hdrsize + (iMsgAreaNumber * (msgshdr.recsize + msgshdr.syssize));
if (fseek(pAreas, offset, 0) != 0) {
WriteError("$Can't move pointer in %s", temp);
}
fread(&msgs, msgshdr.recsize, 1, pAreas);
if ((Access(exitinfo.Security, msgs.RDSec)) && (msgs.Active) && (strlen(msgs.Password) == 0)) {
break;
}
}
}
SetMsgArea(iMsgAreaNumber);
Syslog('+', "Msg area %lu %s", iMsgAreaNumber, sMsgAreaDesc);
free(temp);
fclose(pAreas);
return;
}
}
clear();
Enter(1);
/* Message Areas */
pout(CFG.HiliteF, CFG.HiliteB, (char *) Language(231));
Enter(2);
fseek(pAreas, msgshdr.hdrsize, 0);
while (fread(&msgs, msgshdr.recsize, 1, pAreas) == 1) {
/*
* Skip the echomail systems
*/
fseek(pAreas, msgshdr.syssize, SEEK_CUR);
if ((Access(exitinfo.Security, msgs.RDSec)) && (msgs.Active)) {
msgs.Name[31] = '\0';
snprintf(msg, 81, "%5d", Recno + 1);
pout(WHITE, BLACK, msg);
colour(LIGHTBLUE, BLACK);
/* Check for New Mail if N was put on option data */
if (iCheckNew) {
if (Msg_Open(msgs.Base)) {
MsgBase.Highest = Msg_Highest();
LR.UserID = grecno;
if (Msg_GetLastRead(&LR) != TRUE) {
LR.HighReadMsg = 0;
}
if (MsgBase.Highest > LR.HighReadMsg ) {
pout(YELLOW, BLACK, (char *)" * ");
} else {
PUTSTR((char *)" . ");
}
Msg_Close();
} else {
PUTSTR((char *)" . ");
}
} else {
PUTSTR((char *)" . ");
}
snprintf(msg, 81, "%-31s", msgs.Name);
pout(CYAN, BLACK, msg);
iAreaCount++;
if ((iAreaCount % 2) == 0)
Enter(1);
else
PUTCHAR(' ');
}
Recno++;
if ((iAreaCount / 2) == rows) {
/* More (Y/n/=/Area #): */
pout(CFG.MoreF, CFG.MoreB, (char *) Language(207));
/*
* Ask user for Area or enter to continue
*/
colour(CFG.InputColourF, CFG.InputColourB);
GetstrC(temp, 7);
if (toupper(temp[0]) == Keystroke(207, 1))
break;
if ((strcmp(temp, "")) != 0) {
iGotArea = TRUE;
break;
}
iAreaCount = 2;
}
}
/*
* If user type in area above during area listing
* don't ask for it again
*/
if (!iGotArea) {
Enter(1);
/* Select Area: */
pout(CFG.HiliteF, CFG.HiliteB, (char *) Language(232));
colour(CFG.InputColourF, CFG.InputColourB);
GetstrC(temp, 80);
}
/*
* Check if user pressed ENTER
*/
if ((strcmp(temp, "")) == 0) {
fclose(pAreas);
free(temp);
return;
}
iMsgAreaNumber = atoi(temp);
iMsgAreaNumber--;
/*
* Do a check in case user presses Zero
*/
if (iMsgAreaNumber == -1)
iMsgAreaNumber = 0;
offset = msgshdr.hdrsize + (iMsgAreaNumber * (msgshdr.recsize + msgshdr.syssize));
if (fseek(pAreas, offset, 0) != 0) {
WriteError("$Can't move pointer in mareas.data.");
}
fread(&msgs, msgshdr.recsize, 1, pAreas);
/*
* Do a check if area is greater or less number than allowed,
* security acces (level, flags and age) is oke, and the area
* is active.
*/
if (iMsgAreaNumber > iAreaNum || iMsgAreaNumber < 0 || (Access(exitinfo.Security, msgs.RDSec) == FALSE) || (!msgs.Active)) {
Enter(1);
/*
* Invalid area specified - Please try again ...
*/
pout(LIGHTRED, BLACK, (char *) Language(233));
Enter(2);
Pause();
fclose(pAreas);
iMsgAreaNumber = iOldArea;
SetMsgArea(iMsgAreaNumber);
free(temp);
return;
}
SetMsgArea(iMsgAreaNumber);
Syslog('+', "Msg area %lu %s", iMsgAreaNumber, sMsgAreaDesc);
/*
* Check if msg area has a password, if it does ask user for it
*/
if ((strlen(msgs.Password)) > 2) {
Enter(2);
/* Please enter Area Password: */
pout(WHITE, BLACK, (char *) Language(233));
colour(CFG.InputColourF, CFG.InputColourB);
GetstrC(temp, 20);
if ((strcmp(temp, msgs.Password)) != 0) {
Enter(1);
/* Password is incorrect */
pout(WHITE, BLACK, (char *) Language(234));
Syslog('!', "Incorrect Message Area # %d password given: %s", iMsgAreaNumber, temp);
SetMsgArea(iOldArea);
} else {
Enter(1);
/* Password is correct */
pout(WHITE, BLACK, (char *) Language(235));
Enter(2);
}
Pause();
}
free(temp);
fclose(pAreas);
}
void Delete_MsgNum(unsigned int MsgNum)
{
int Result = FALSE;
/* Deleting message */
pout(LIGHTRED, BLACK, (char *) Language(230));
if (Msg_Open(sMsgAreaBase)) {
if (Msg_Lock(15L)) {
Result = Msg_Delete(MsgNum);
Msg_UnLock();
}
Msg_Close();
}
if (Result)
Syslog('+', "Deleted msg #%lu in Area #%d (%s)", MsgNum, iMsgAreaNumber, sMsgAreaDesc);
else
WriteError("ERROR delete msg #%lu in Area #%d (%s)", MsgNum, iMsgAreaNumber, sMsgAreaDesc);
}
/*
* This Function checks to see if the user exists in the user database
* and returns a int TRUE or FALSE
*/
int CheckUser(char *To)
{
FILE *pUsrConfig;
int Found = FALSE;
char *temp;
int offset;
unsigned int Crc;
temp = calloc(PATH_MAX, sizeof(char));
snprintf(temp, PATH_MAX, "%s/etc/users.data", getenv("MBSE_ROOT"));
if ((pUsrConfig = fopen(temp,"rb")) == NULL) {
WriteError("$Can't open file %s for reading", temp);
Pause();
free(temp);
return FALSE;
}
free(temp);
fread(&usrconfighdr, sizeof(usrconfighdr), 1, pUsrConfig);
Crc = StringCRC32(tl(To));
while (fread(&usrconfig, usrconfighdr.recsize, 1, pUsrConfig) == 1) {
if (StringCRC32(tl(usrconfig.sUserName)) == Crc) {
Found = TRUE;
break;
}
}
if (!Found)
Syslog('!', "User attempted to mail unknown user: %s", tlcap(To));
/*
* Restore users record
*/
offset = usrconfighdr.hdrsize + (grecno * usrconfighdr.recsize);
if (fseek(pUsrConfig, offset, 0) != 0)
WriteError("$Can't move pointer there.");
else
fread(&usrconfig, usrconfighdr.recsize, 1, pUsrConfig);
fclose(pUsrConfig);
return Found;
}
/*
* Check for new mail
*/
void CheckMail()
{
FILE *pMsgArea, *Tmp;
int x, Found = 0, Color, Count = 0, Reading, OldMsgArea;
char *temp, *sFileName, msg[81];
unsigned int i, Start;
typedef struct _Mailrec {
int Area;
unsigned int Msg;
} _Mail;
_Mail Mail;
lastread LR;
OldMsgArea = iMsgAreaNumber;
iMsgAreaNumber = 0;
Syslog('+', "Start checking for new mail");
clear();
/* Checking your mail box ... */
language(LIGHTGREEN, BLACK, 150);
Enter(2);
Color = LIGHTBLUE;
/*
* Open temporary file
*/
if ((Tmp = tmpfile()) == NULL) {
WriteError("$unable to open temporary file");
return;
}
/*
* First check the e-mail mailbox
*/
temp = calloc(PATH_MAX, sizeof(char));
if (exitinfo.Email && strlen(exitinfo.Password)) {
check_popmail(exitinfo.Name, exitinfo.Password);
colour(Color++, BLACK);
PUTCHAR('\r');
PUTSTR((char *)"e-mail Private e-mail mailbox");
Count = 0;
snprintf(temp, PATH_MAX, "%s/%s/mailbox", CFG.bbs_usersdir, exitinfo.Name);
SetEmailArea((char *)"mailbox");
if (Msg_Open(temp)) {
/*
* Check lastread pointer, if none found start
* at the begin of the messagebase.
*/
LR.UserID = grecno;
if (Msg_GetLastRead(&LR))
Start = LR.HighReadMsg + 1;
else
Start = EmailBase.Lowest;
for (i = Start; i <= EmailBase.Highest; i++) {
if (Msg_ReadHeader(i)) {
/*
* Only check the received status of the email. The mail
* may not be direct addressed to this user (aliases database)
* but if it is in his mailbox it is always for the user.
* FIXME: mail written by the user is shown as new too.
*/
if (!Msg.Received) {
/*
* Store area and message number in temporary file.
*/
Mail.Area = -1; /* Means e-mail mailbox */
Mail.Msg = Msg.Id + EmailBase.Lowest -1;
fwrite(&Mail, sizeof(Mail), 1, Tmp);
Count++;
Found++;
}
}
}
Msg_Close();
}
if (Count) {
Enter(2);
/* messages in */
snprintf(temp, 81, "%d %s private e-mail mailbox", Count, (char *)Language(213));
pout(CFG.TextColourF, CFG.TextColourB, temp);
Enter(2);
Syslog('m', " %d messages in private e-mail mailbox", Count);
}
}
/*
* Open the message base configuration
*/
sFileName = calloc(PATH_MAX, sizeof(char));
snprintf(sFileName, PATH_MAX, "%s/etc/mareas.data", getenv("MBSE_ROOT"));
if((pMsgArea = fopen(sFileName, "r+")) == NULL) {
WriteError("$Can't open: %s", sFileName);
free(temp);
free(sFileName);
return;
}
fread(&msgshdr, sizeof(msgshdr), 1, pMsgArea);
/*
* Check all normal areas one by one
*/
while (fread(&msgs, msgshdr.recsize, 1, pMsgArea) == 1) {
fseek(pMsgArea, msgshdr.syssize, SEEK_CUR);
if ((msgs.Active) && (exitinfo.Security.level >= msgs.RDSec.level)) {
SetMsgArea(iMsgAreaNumber);
snprintf(temp, 81, "%d", iMsgAreaNumber + 1);
if (Color < WHITE)
Color++;
else
Color = LIGHTBLUE;
PUTCHAR('\r');
snprintf(msg, 81, "%6s %-40s", temp, sMsgAreaDesc);
pout(Color, BLACK, msg);
Count = 0;
/*
* Refresh timers
*/
Nopper();
alarm_on();
if (Msg_Open(sMsgAreaBase)) {
/*
* Check lastread pointer, if none found start
* at the begin of the messagebase.
*/
LR.UserID = grecno;
if (Msg_GetLastRead(&LR))
Start = LR.HighReadMsg + 1;
else
Start = MsgBase.Lowest;
for (i = Start; i <= MsgBase.Highest; i++) {
if (Msg_ReadHeader(i)) {
if ((!Msg.Received) && (IsMe(Msg.To))) {
/*
* Store area and message number
* in temporary file.
*/
Mail.Area = iMsgAreaNumber;
Mail.Msg = Msg.Id + MsgBase.Lowest -1;
fwrite(&Mail, sizeof(Mail), 1, Tmp);
Count++;
Found++;
}
}
}
Msg_Close();
}
if (Count) {
Enter(2);
/* messages in */
snprintf(msg, 81, "%d %s %s", Count, (char *)Language(213), sMsgAreaDesc);
pout(CFG.TextColourF, CFG.TextColourB, msg);
Enter(2);
Syslog('m', " %d messages in %s", Count, sMsgAreaDesc);
}
}
iMsgAreaNumber++;
}
fclose(pMsgArea);
PUTCHAR('\r');
for (i = 0; i < 48; i++)
PUTCHAR(' ');
PUTCHAR('\r');
if (Found) {
Enter(1);
/* You have messages, read your mail now? [Y/n]: */
snprintf(msg, 81, "%s%d %s", (char *) Language(142), Found, (char *) Language(143));
pout(YELLOW, BLACK, msg);
colour(CFG.InputColourF, CFG.InputColourB);
alarm_on();
if (toupper(Readkey()) != Keystroke(143,1)) {
rewind(Tmp);
Reading = TRUE;
while ((Reading) && (fread(&Mail, sizeof(Mail), 1, Tmp) == 1)) {
if (Mail.Area == -1) {
/*
* Private e-mail
*/
Read_a_Email(Mail.Msg);
} else {
SetMsgArea(Mail.Area);
Read_a_Msg(Mail.Msg, FALSE);
}
/* (R)eply, (N)ext, (Q)uit */
pout(CFG.CRColourF, CFG.CRColourB, (char *)Language(218));
alarm_on();
x = toupper(Readkey());
if (x == Keystroke(218, 0)) {
Syslog('m', " Reply!");
if (Mail.Area == -1) {
Reply_Email(TRUE);
} else {
Reply_Msg(TRUE);
}
}
if (x == Keystroke(218, 2)) {
Syslog('m', " Quit check for new mail");
iMsgAreaNumber = OldMsgArea;
fclose(Tmp);
SetMsgArea(OldMsgArea);
Enter(2);
free(temp);
free(sFileName);
return;
}
}
}
} else {
language(LIGHTRED, BLACK, 144);
Enter(1);
sleep(3);
} /* if (Found) */
iMsgAreaNumber = OldMsgArea;
fclose(Tmp);
SetMsgArea(OldMsgArea);
Enter(2);
free(temp);
free(sFileName);
}
/*
* Status of all mail areas.
*/
void MailStatus()
{
FILE *pMsgArea;
int Count = 0;
int OldMsgArea;
char temp[81], msg[81];
char *sFileName;
unsigned int i;
sFileName = calloc(PATH_MAX, sizeof(char));
OldMsgArea = iMsgAreaNumber;
iMsgAreaNumber = 0;
clear();
/* Area Type Description Messages Personal */
snprintf(msg, 81, "%-79s", (char *)Language(226));
pout(YELLOW, BLUE, msg);
Enter(1);
iLineCount = 2;
if (exitinfo.Email) {
snprintf(temp, 81, "%s", sMailbox);
for (i = 0; i < 3; i++) {
switch (i) {
case 0: SetEmailArea((char *)"mailbox");
break;
case 1: SetEmailArea((char *)"archive");
break;
case 2: SetEmailArea((char *)"trash");
break;
}
pout(LIGHTRED, BLACK, (char *)" Email");
snprintf(msg, 81, " %-40s", Language(467 + i));
pout(LIGHTCYAN, BLACK, msg);
if (EmailBase.Highest)
snprintf(msg, 81, " %8u", EmailBase.Highest - EmailBase.Lowest + 1);
else
snprintf(msg, 81, " 0");
pout(YELLOW, BLACK, msg);
if (EmailBase.Highest)
snprintf(msg, 81, " %8u", EmailBase.Highest - EmailBase.Lowest + 1);
else
snprintf(msg, 81, " 0");
pout(LIGHTBLUE, BLACK, msg);
Enter(1);
}
iLineCount = 5;
SetEmailArea(temp);
}
/*
* Open the message base configuration
*/
snprintf(sFileName, PATH_MAX, "%s/etc/mareas.data", getenv("MBSE_ROOT"));
if((pMsgArea = fopen(sFileName, "r+")) == NULL) {
WriteError("Can't open file: %s", sFileName);
free(sFileName);
return;
}
fread(&msgshdr, sizeof(msgshdr), 1, pMsgArea);
/*
* Check all areas one by one
*/
while (fread(&msgs, msgshdr.recsize, 1, pMsgArea) == 1) {
fseek(pMsgArea, msgshdr.syssize, SEEK_CUR);
if ((msgs.Active) && (exitinfo.Security.level >= msgs.RDSec.level)) {
SetMsgArea(iMsgAreaNumber);
snprintf(temp, 81, "%d", iMsgAreaNumber + 1);
snprintf(msg, 81, "%5s", temp);
pout(WHITE, BLACK, msg);
colour(LIGHTRED, BLACK);
switch(msgs.Type) {
case LOCALMAIL: PUTSTR((char *)" Local");
break;
case NETMAIL: PUTSTR((char *)" Net ");
break;
case LIST:
case ECHOMAIL: PUTSTR((char *)" Echo ");
break;
case NEWS: PUTSTR((char *)" News ");
break;
}
snprintf(msg, 81, " %-40s", sMsgAreaDesc);
pout(LIGHTCYAN, BLACK, msg);
Count = 0;
if (Msg_Open(sMsgAreaBase)) {
for (i = MsgBase.Lowest; i <= MsgBase.Highest; i++) {
if (Msg_ReadHeader(i)) {
if (IsMe(Msg.To) || IsMe(Msg.From))
Count++;
}
}
Msg_Close();
} else
WriteError("Error open JAM %s", sMsgAreaBase);
if (MsgBase.Highest)
snprintf(msg, 81, " %8u", MsgBase.Highest - MsgBase.Lowest + 1);
else
snprintf(msg, 81, " 0");
pout(YELLOW, BLACK, msg);
snprintf(msg, 81, " %8d", Count);
pout(LIGHTBLUE, BLACK, msg);
Enter(1);
if (LC(1))
break;
}
iMsgAreaNumber++;
}
fclose(pMsgArea);
SetMsgArea(OldMsgArea);
free(sFileName);
Pause();
}
/*
* Set message area number, set global area description and JAM path
*/
void SetMsgArea(unsigned int AreaNum)
{
FILE *pMsgArea;
int offset;
char *sFileName;
sFileName = calloc(PATH_MAX, sizeof(char));
snprintf(sFileName, PATH_MAX, "%s/etc/mareas.data", getenv("MBSE_ROOT"));
memset(&msgs, 0, sizeof(msgs));
if ((pMsgArea = fopen(sFileName, "r")) == NULL) {
WriteError("$Can't open file: %s", sFileName);
free(sFileName);
return;
}
fread(&msgshdr, sizeof(msgshdr), 1, pMsgArea);
offset = msgshdr.hdrsize + (AreaNum * (msgshdr.recsize + msgshdr.syssize));
if (fseek(pMsgArea, offset, 0) != 0) {
WriteError("$Can't move pointer in %s",sFileName);
free(sFileName);
return;
}
fread(&msgs, msgshdr.recsize, 1, pMsgArea);
strcpy(sMsgAreaDesc, msgs.Name);
strcpy(sMsgAreaBase, msgs.Base);
iMsgAreaNumber = AreaNum;
iMsgAreaType = msgs.Type;
fclose(pMsgArea);
/*
* Get information from the message base
*/
if (Msg_Open(sMsgAreaBase)) {
MsgBase.Lowest = Msg_Lowest();
MsgBase.Highest = Msg_Highest();
MsgBase.Total = Msg_Number();
Msg_Close();
} else
WriteError("Error open JAM %s", sMsgAreaBase);
free(sFileName);
}
/*
* External Message Editor
*/
int Ext_Edit()
{
int changed;
int j, i;
char *l, *tmpname;
FILE *fd;
struct stat st1,st2;
changed=FALSE;
tmpname = calloc(PATH_MAX, sizeof(char));
snprintf(tmpname, PATH_MAX, "%s/%s/data.msg", CFG.bbs_usersdir, exitinfo.Name);
if ((fd = fopen(tmpname, "w")) == NULL) {
Syslog('+',"EXT_EDIT: Unable to open %s for writing", tmpname);
} else {
fprintf(fd,"AREA='%s'\n",sMsgAreaDesc);
fprintf(fd,"AREANUM='%d'\n",iMsgAreaNumber+1);
fprintf(fd,"AREATYPE='%d'\n",iMsgAreaType);
fprintf(fd,"MSGFROM='%s'\n",Msg.From);
fprintf(fd,"MSGFROMADDR='%s'\n",Msg.FromAddress);
fprintf(fd,"MSGTO='%s'\n",Msg.To);
fprintf(fd,"MSGTOADDR='%s'\n",Msg.ToAddress);
fprintf(fd,"MSGSUBJECT='%s'\n",Msg.Subject);
fprintf(fd,"BBSLANGUAGE='%c'\n",exitinfo.iLanguage);
fprintf(fd,"BBSFSEDKEYS='%d'\n",exitinfo.FSemacs);
fprintf(fd,"LINES='%d'\n",rows);
fprintf(fd,"COLUMNS='80'\n");
fprintf(fd,"UTF8='%s'\n",utf8?"Yes":"No");
fclose(fd);
}
snprintf(tmpname, PATH_MAX, "%s/%s/edit.msg", CFG.bbs_usersdir, exitinfo.Name);
if ((fd = fopen(tmpname, "w")) == NULL) {
Syslog('+',"EXT_EDIT: Unable to open %s for writing", tmpname);
} else {
for (i = 1; i <= Line; i++) {
fprintf(fd,"%s\n",Message[i]);
}
fclose(fd);
stat( tmpname, &st1 );
ExtDoor(CFG.externaleditor,FALSE,TRUE,TRUE,FALSE,TRUE, FALSE, (char *)"Write message");
if (rawport() != 0) {
WriteError("Unable to set raw mode");
}
stat( tmpname, &st2 );
}
if ( st1.st_mtime != st2.st_mtime ) {
l = calloc(81, sizeof(char));
if ((fd = fopen(tmpname, "r")) == NULL) {
Syslog('+',"EXT_EDIT: Unable to open %s for reading", tmpname);
} else {
i=1;
while ( (fgets(l,80,fd) != NULL) && (i < TEXTBUFSIZE ) ) {
for (j = 0; i < strlen(l); j++) {
if (*(l + j) == '\0')
break;
if (*(l + j) == '\n')
*(l + j) = '\0';
if (*(l + j) == '\r')
*(l + j) = '\0';
/*
* Make sure that any tear or origin lines are
* made invalid.
*/
if (strncmp(l, (char *)"--- ", 4) == 0)
l[1] = 'v';
if (strncmp(l, (char *)" * Origin:", 10) == 0)
l[1] = '+';
}
snprintf(Message[i], TEXTBUFSIZE +1, "%s",l);
i++;
}
changed=TRUE;
Line=i;
fclose(fd);
}
free(l);
}
free(tmpname);
return changed;
}