1220 lines
36 KiB
C++
1220 lines
36 KiB
C++
|
|
// ------------------------------------------------------------------
|
|
// GoldED+
|
|
// Copyright (C) 1990-1999 Odinn Sorensen
|
|
// Copyright (C) 1999-2000 Alexander S. Aganichev
|
|
// ------------------------------------------------------------------
|
|
// This program 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 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program 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 this program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
|
// MA 02111-1307 USA
|
|
// ------------------------------------------------------------------
|
|
// $Id$
|
|
// ------------------------------------------------------------------
|
|
// Mega-functions to handle creation of msgs :-(
|
|
// ------------------------------------------------------------------
|
|
|
|
#include <golded.h>
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
static std::vector<int> post_xparea;
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
std::string &strtrimline(std::string &p) {
|
|
|
|
if(not p.empty()) {
|
|
std::string::iterator trail = p.end();
|
|
while(trail != p.begin()) {
|
|
--trail;
|
|
if(not strchr(" \t\r\n", *trail) and not issoftcr(*trail)) {
|
|
++trail;
|
|
break;
|
|
}
|
|
}
|
|
p.erase(trail, p.end());
|
|
}
|
|
return p;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
const char* get_subject_re_info(const char* s, ulong& n) {
|
|
|
|
if(toupper(*s) == 'R' and tolower(s[1]) == 'e') {
|
|
|
|
if(s[2] == ':') {
|
|
n = 1;
|
|
return s + 3;
|
|
}
|
|
else if(s[2] == '^' and isdigit(s[3])) {
|
|
const char* d = s + 4;
|
|
while(isdigit(*d))
|
|
d++;
|
|
if(*d == ':') {
|
|
n = (ulong)atol(s + 3);
|
|
return d + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
n = 0;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
void CheckSubject(GMsg* msg, char* subj) {
|
|
|
|
for(int n=0; n<fspecs; n++)
|
|
throw_release(fspec[n].fblk);
|
|
fspecs = specfiles = 0;
|
|
fspec = (FileSpec*)throw_realloc(fspec, sizeof(FileSpec));
|
|
|
|
if(AA->isnet()) {
|
|
if(EDIT->AutoAttach()) {
|
|
// If the subject was changed
|
|
if(not strieql(subj, msg->re)) {
|
|
// Check if the subj begins with drive spec
|
|
if(not (msg->attr.frq() or msg->attr.att() or msg->attr.urq())) {
|
|
char* ptr = subj;
|
|
if(*ptr == '^')
|
|
ptr++;
|
|
#ifdef __UNIX__
|
|
if((*ptr == '/') and not strchr(ptr, ':')) { /* } */
|
|
#else
|
|
if((in_range((char)tolower(*ptr), (char)'c', (char)'z') and (ptr[1] == ':')) or ((*ptr == '\\') and (ptr[1] == '\\'))) {
|
|
#endif
|
|
// Autoattach
|
|
msg->attr.att1();
|
|
AttrAdd(&msg->attr, &CFG->attribsattach);
|
|
DispHeadAttrs(msg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(msg->attr.frq()) {
|
|
// Handle File Requests
|
|
if(*subj) {
|
|
ISub buf;
|
|
strcpy(buf, subj);
|
|
char* ptr = strtok(buf, " ");
|
|
while(ptr) {
|
|
if(*ptr == '!') { // Store password
|
|
if(fspecs)
|
|
strcpy(fspec[fspecs-1].password, ptr);
|
|
}
|
|
else {
|
|
fspec = (FileSpec*)throw_realloc(fspec, (fspecs+1)*sizeof(FileSpec));
|
|
memset(fspec+fspecs, 0, sizeof(FileSpec));
|
|
fspec[fspecs].files = 1;
|
|
strxcpy(fspec[fspecs].path, ptr, sizeof(Path));
|
|
specfiles++;
|
|
fspecs++;
|
|
}
|
|
ptr = strtok(NULL, " ");
|
|
}
|
|
}
|
|
}
|
|
else if(msg->attr.att()) {
|
|
// Handle File Attaching
|
|
ISub buf;
|
|
if(strblank(subj)) {
|
|
if(*CFG->attachpath)
|
|
strxcpy(subj, CFG->attachpath, sizeof(ISub));
|
|
else
|
|
strxcpy(subj, CFG->goldpath, sizeof(ISub));
|
|
}
|
|
strcpy(buf, subj);
|
|
char* ptr = strtok(buf, " ");
|
|
while(ptr) {
|
|
fspec = (FileSpec*)throw_realloc(fspec, (fspecs+1)*sizeof(FileSpec));
|
|
memset(fspec+fspecs, 0, sizeof(FileSpec));
|
|
if(*ptr == '^') {
|
|
fspec[fspecs].delsent = YES;
|
|
ptr++;
|
|
}
|
|
if((ptr == CleanFilename(ptr)) and *CFG->attachpath)
|
|
strxmerge(fspec[fspecs].path, sizeof(Path), CFG->attachpath, ptr, NULL);
|
|
else
|
|
strxcpy(fspec[fspecs].path, ptr, sizeof(Path));
|
|
FileSelect(msg, LNG->AttachFiles, &fspec[fspecs]);
|
|
specfiles += fspec[fspecs].files;
|
|
if(fspec[fspecs].files)
|
|
fspecs++;
|
|
ptr = strtok(NULL, " ");
|
|
}
|
|
}
|
|
else if(msg->attr.urq()) {
|
|
// Handle Update Requests
|
|
ISub buf;
|
|
if(not *subj)
|
|
strcpy(subj, ".");
|
|
strcpy(buf, subj);
|
|
char* ptr = strtok(buf, " ");
|
|
while(ptr) {
|
|
if(*ptr == '!') { // Store password
|
|
if(fspecs)
|
|
strcpy(fspec[fspecs-1].password, ptr);
|
|
}
|
|
else {
|
|
fspec = (FileSpec*)throw_realloc(fspec, (fspecs+1)*sizeof(FileSpec));
|
|
memset(fspec+fspecs, 0, sizeof(FileSpec));
|
|
strxcpy(fspec[fspecs].path, ptr, sizeof(Path));
|
|
FileSelect(msg, LNG->UpdreqFiles, &fspec[fspecs]);
|
|
specfiles += fspec[fspecs].files;
|
|
if(fspec[fspecs].files)
|
|
fspecs++;
|
|
}
|
|
ptr = strtok(NULL, " ");
|
|
}
|
|
}
|
|
|
|
// Generate the (first) processed subject line
|
|
if(specfiles) {
|
|
ISub buf;
|
|
*buf = NUL;
|
|
for(int x=0,n=0; x<fspecs; x++) {
|
|
for(int m=0; m<fspec[x].files; m++) {
|
|
ISub subject;
|
|
sprintf(subject, "%s%s%s%s%s ", fspec[x].delsent ? "^" : "", ReMapPath(fspec[x].path), fspec[x].fblk ? (fspec[x].fblk[m].name ? fspec[x].fblk[m].name : "") : "", *fspec[x].password ? " " : "", fspec[x].password);
|
|
if((strlen(buf) + strlen(subject)) > 71) {
|
|
n++;
|
|
break;
|
|
}
|
|
else
|
|
strcat(buf, subject);
|
|
}
|
|
if(n)
|
|
break;
|
|
}
|
|
strtrim(strcpy(subj, buf));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
static bool have_origin(GMsg *msg) {
|
|
|
|
Line* line = msg->lin;
|
|
|
|
while(line)
|
|
if(line->type & GLINE_ORIG)
|
|
return true;
|
|
else
|
|
line = line->next;
|
|
return false;
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
static void MakeMsg3(int& mode, GMsg* msg) {
|
|
|
|
int n;
|
|
std::vector<gaka>::iterator u;
|
|
|
|
msg->charsetlevel = 0;
|
|
if(*AA->Xlatexport()) {
|
|
msg->charsetlevel = LoadCharset(CFG->xlatlocalset, AA->Xlatexport());
|
|
if(msg->charsetlevel)
|
|
sprintf(msg->charset, "%s %d", AA->Xlatexport(), msg->charsetlevel);
|
|
else
|
|
sprintf(msg->charset, "%s 2", CFG->xlatlocalset);
|
|
}
|
|
|
|
// Do Timefields
|
|
if(msg->attr.fmu()) {
|
|
time_t a = time(NULL);
|
|
struct tm *tp = gmtime(&a);
|
|
tp->tm_isdst = -1;
|
|
time_t b = mktime(tp);
|
|
a += a - b;
|
|
if(AA->isjam() or AA->iswildcat())
|
|
msg->received = a;
|
|
else
|
|
msg->arrived = a;
|
|
}
|
|
|
|
GMsg* carbon=NULL;
|
|
int cc = DoCarboncopy(msg, &carbon);
|
|
|
|
if(AA->isecho() or have_origin(msg))
|
|
DoTearorig(mode, msg);
|
|
|
|
// Do FakeNets
|
|
msg->oorig = msg->orig;
|
|
if(AA->isnet() and msg->orig.point) {
|
|
for(u = CFG->aka.begin(); u != CFG->aka.end(); u++) {
|
|
if(not memcmp(&(*u), &msg->orig, sizeof(Addr))) {
|
|
// Use fakenet to everybody
|
|
if(u->pointnet) {
|
|
msg->oorig.net = u->pointnet; // Create fake address
|
|
msg->oorig.node = msg->orig.point;
|
|
msg->oorig.point = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
DoKludges(mode, msg);
|
|
|
|
if(cc) {
|
|
std::string temp;
|
|
update_statuslinef(LNG->StatusCC, msg->to, msg->dest.make_string(temp).c_str());
|
|
}
|
|
|
|
msg->LinesToText();
|
|
|
|
if(AA->isnet() and (msg->attr.frq() or msg->attr.att() or msg->attr.urq()) and specfiles >= 1)
|
|
CreateFileMsgs(mode, msg);
|
|
else {
|
|
HeaderView->Use(AA, msg);
|
|
HeaderView->Paint();
|
|
|
|
// Do zoneGating
|
|
if(AA->isnet() and CFG->zonegating and (msg->dest.zone != msg->orig.zone)) {
|
|
if(not (msg->attr.dir() or msg->attr.cra() or msg->attr.hld())) {
|
|
GMenuZonegate MenuZonegate;
|
|
if(CFG->zonegating == YES or MenuZonegate.Run())
|
|
ZonegateIt(msg->odest, msg->orig, msg->dest);
|
|
}
|
|
}
|
|
|
|
if(msg->odest.net == 0)
|
|
msg->odest = msg->dest;
|
|
|
|
HeaderView->Use(AA, msg);
|
|
HeaderView->Paint();
|
|
w_info(LNG->Wait);
|
|
AA->SaveMsg((mode == MODE_CHANGE) ? GMSG_UPDATE : GMSG_NEW, msg);
|
|
if(not (AA->isecho() and mode == MODE_REPLYCOMMENT))
|
|
update_addressbook(msg);
|
|
w_info(NULL);
|
|
}
|
|
|
|
if(AL.AreaEchoToNo(AA->Areayouwroteto())>=0) {
|
|
Area* A = AL.AreaEchoToPtr(AA->Areayouwroteto());
|
|
if(AA != A) {
|
|
char* back = NULL;
|
|
uint mlen = 0;
|
|
|
|
msg->link.reset();
|
|
char* ptr = msg->txt + (*msg->txt == CTRL_A);
|
|
if(not strnieql(ptr, "AREA:", 5)) {
|
|
uint elen = 6 + strlen(AA->echoid()) + 1;
|
|
mlen = strlen(msg->txt)+1;
|
|
msg->txt = (char*)throw_realloc(msg->txt, elen+mlen);
|
|
back = msg->txt+elen;
|
|
memmove(back, msg->txt, mlen);
|
|
strcpy(msg->txt, "\001AREA:");
|
|
strcpy(msg->txt+6, AA->echoid());
|
|
msg->txt[elen-1] = CR;
|
|
}
|
|
AA->Close();
|
|
A->Open();
|
|
ulong oldmsgno = msg->msgno;
|
|
A->NewMsgno(msg);
|
|
A->SaveMsg(GMSG_NEW|GMSG_NOLSTUPD, msg);
|
|
A->Close();
|
|
AA->Open();
|
|
if(back)
|
|
memmove(msg->txt, back, mlen);
|
|
msg->msgno = oldmsgno;
|
|
}
|
|
}
|
|
|
|
if(cc) {
|
|
|
|
Area* A = AA;
|
|
|
|
if(AA->isecho()) {
|
|
AA = AL.AreaEchoToPtr(AA->Areareplyto());
|
|
if(AA and (AA != A)) {
|
|
AA->Open();
|
|
AA->RandomizeData(mode);
|
|
}
|
|
else
|
|
AA = A;
|
|
}
|
|
|
|
for(n=0; n<cc; n++) {
|
|
GMsg* cmsg = &carbon[n];
|
|
cmsg->txt = msg->txt;
|
|
cmsg->lin = msg->lin;
|
|
cmsg->line = msg->line;
|
|
cmsg->lines = msg->lines;
|
|
cmsg->messageid = msg->messageid;
|
|
cmsg->inreplyto = msg->inreplyto;
|
|
cmsg->references = msg->references;
|
|
AA->NewMsgno(cmsg);
|
|
{
|
|
std::string temp;
|
|
update_statuslinef(LNG->StatusCC, cmsg->to, cmsg->dest.make_string(temp).c_str());
|
|
}
|
|
|
|
// Do aka matching
|
|
if(AA->Akamatching()) {
|
|
int newaka = AkaMatch(&cmsg->orig, &cmsg->dest);
|
|
cmsg->orig = CFG->aka[newaka].addr;
|
|
}
|
|
|
|
// Do FakeNets
|
|
cmsg->oorig = cmsg->orig;
|
|
if(AA->isnet() and cmsg->orig.point) {
|
|
for(u = CFG->aka.begin(); u != CFG->aka.end(); u++) {
|
|
if(not memcmp(&(*u), &cmsg->orig, sizeof(Addr))) {
|
|
// Use fakenet to everybody
|
|
if(u->pointnet) {
|
|
cmsg->oorig.net = u->pointnet; // Create fake address
|
|
cmsg->oorig.node = cmsg->orig.point;
|
|
cmsg->oorig.point = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// remove unnessesary To: line from message if present
|
|
for(Line* line = cmsg->lin; line; line = line->next)
|
|
if(strnieql(line->txt.c_str(), "To:", 3))
|
|
line = DeleteLine(line);
|
|
else if(line->type & GLINE_KLUDGE)
|
|
continue;
|
|
else
|
|
break;
|
|
|
|
DoKludges(mode, cmsg);
|
|
|
|
if(have_origin(cmsg))
|
|
DoTearorig(mode, cmsg);
|
|
|
|
cmsg->LinesToText();
|
|
msg->txt = cmsg->txt;
|
|
msg->lin = cmsg->lin;
|
|
msg->line = cmsg->line;
|
|
msg->lines = cmsg->lines;
|
|
msg->messageid = cmsg->messageid;
|
|
msg->inreplyto = cmsg->inreplyto;
|
|
msg->references = cmsg->references;
|
|
if(AA->isnet() and (cmsg->attr.frq() or cmsg->attr.att() or cmsg->attr.urq())) {
|
|
CreateFileMsgs(MODE_NEW, cmsg);
|
|
msg->txt = cmsg->txt;
|
|
msg->lin = cmsg->lin;
|
|
msg->line = cmsg->line;
|
|
msg->lines = cmsg->lines;
|
|
msg->messageid = cmsg->messageid;
|
|
msg->inreplyto = cmsg->inreplyto;
|
|
msg->references = cmsg->references;
|
|
}
|
|
else {
|
|
HeaderView->Use(AA, cmsg);
|
|
HeaderView->Paint();
|
|
|
|
// Do zoneGating
|
|
if(AA->isnet() and CFG->zonegating and (cmsg->dest.zone != cmsg->orig.zone)) {
|
|
if(not cmsg->attr.dir()) {
|
|
GMenuZonegate MenuZonegate;
|
|
if(CFG->zonegating == YES or MenuZonegate.Run())
|
|
ZonegateIt(cmsg->odest, cmsg->orig, cmsg->dest);
|
|
}
|
|
}
|
|
|
|
if(cmsg->odest.net == 0)
|
|
cmsg->odest = cmsg->dest;
|
|
|
|
HeaderView->Use(AA, cmsg);
|
|
HeaderView->Paint();
|
|
w_info(LNG->Wait);
|
|
AA->SaveMsg(GMSG_NEW, cmsg);
|
|
w_info(NULL);
|
|
update_addressbook(cmsg);
|
|
msg->txt = cmsg->txt;
|
|
msg->lin = cmsg->lin;
|
|
msg->line = cmsg->line;
|
|
msg->lines = cmsg->lines;
|
|
msg->messageid = cmsg->messageid;
|
|
msg->inreplyto = cmsg->inreplyto;
|
|
msg->references = cmsg->references;
|
|
}
|
|
}
|
|
throw_release(carbon);
|
|
|
|
if(A != AA) {
|
|
AA->Close();
|
|
AA = A;
|
|
AA->RandomizeData();
|
|
}
|
|
AA->UpdateAreadata();
|
|
}
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
static void MakeMsg2(int& mode, int& status, int& forwstat, int& topline, GMsg* msg, GMsg* oldmsg, GMsg* cmpmsg) {
|
|
|
|
uint n;
|
|
char buf[256], buf2[256];
|
|
Line* line;
|
|
Line* newline;
|
|
|
|
if(status == MODE_CHANGE or (mode == MODE_FORWARD and status == MODE_SAVE)) {
|
|
if(mode == MODE_CHANGE) {
|
|
if(stricmp(msg->By(), AA->Username().name)) {
|
|
std::vector<Node>::iterator x;
|
|
for(n = 0, x = CFG->username.begin(); x != CFG->username.end(); n++, x++) {
|
|
if(strieql(x->name, msg->By())) {
|
|
n = 0;
|
|
break;
|
|
}
|
|
}
|
|
if(n)
|
|
status = MODE_SAVE;
|
|
}
|
|
}
|
|
w_info(LNG->Wait);
|
|
if(mode != MODE_CHANGE) {
|
|
if(not CFG->switches.get(emptytearline))
|
|
strcpy(msg->tearline, AA->Tearline());
|
|
|
|
if(AA->Taglinesupport())
|
|
strcpy(msg->tagline, AA->Tagline());
|
|
|
|
DoTearorig(mode, msg);
|
|
|
|
strcpy(msg->organization, AA->Organization());
|
|
}
|
|
if(mode != MODE_CHANGE or status == MODE_SAVE) {
|
|
if(TemplateToText(mode, msg, oldmsg, AA->Tpl(), OrigArea)) {
|
|
HeaderView->Use(AA, msg);
|
|
HeaderView->Paint();
|
|
}
|
|
msg->attr.pos1();
|
|
msg->TextToLines(CFG->dispmargin-1, false); // Ignore any kludge address found
|
|
msg->attr.pos0();
|
|
}
|
|
uint lineno, position = reader_topline+1;
|
|
for(lineno = 0; lineno < msg->lines; lineno++) {
|
|
if(not (msg->line[lineno]->type & GLINE_QUOT))
|
|
if(msg->line[lineno]->type & GLINE_POSI)
|
|
position = lineno+reader_topline+1;
|
|
}
|
|
if(*EDIT->External() and not EDIT->Internal()) {
|
|
SaveLines(MODE_NEW, AddPath(CFG->goldpath, EDIT->File()), msg, 79);
|
|
}
|
|
int loop = 0;
|
|
w_info(NULL);
|
|
do {
|
|
if(loop) {
|
|
status = MODE_SAVE;
|
|
forwstat = NO;
|
|
}
|
|
if(mode == MODE_FORWARD)
|
|
status = MODE_SAVE;
|
|
if(*EDIT->External() and forwstat == NO and not EDIT->Internal()) {
|
|
strcpy(buf, EDIT->External());
|
|
sprintf(buf2, "%u", position);
|
|
strischg(buf, "@line", buf2);
|
|
strcpy(buf2, AddPath(CFG->goldpath, EDIT->File()));
|
|
strchg(buf2, GOLD_WRONG_SLASH_CHR, GOLD_SLASH_CHR);
|
|
strischg(buf, "@file", buf2);
|
|
long ftbefore = GetFiletime(AddPath(CFG->goldpath, EDIT->File()));
|
|
sprintf(buf2, LNG->EditCmd, buf);
|
|
ShellToDos(buf, buf2, LGREY|_BLACK, YES);
|
|
long ftafter = GetFiletime(AddPath(CFG->goldpath, EDIT->File()));
|
|
if(status != MODE_SAVE) {
|
|
status = MODE_SAVE;
|
|
if(ftbefore == ftafter) {
|
|
if(streql(msg->by, cmpmsg->by))
|
|
if(streql(msg->to, cmpmsg->to))
|
|
if(streql(msg->re, cmpmsg->re))
|
|
if(not memcmp(&msg->attr, &cmpmsg->attr, sizeof(Attr)))
|
|
status = MODE_QUIT;
|
|
}
|
|
}
|
|
if(status != MODE_QUIT) {
|
|
LoadCharset("N/A", "N/A");
|
|
strcpy(stpcpy(msg->charset, CFG->xlatlocalset), " 2");
|
|
msg->charsetlevel = 2;
|
|
}
|
|
}
|
|
else if(forwstat == NO and (EDIT->Internal() or not *EDIT->External())) {
|
|
w_info(LNG->Wait);
|
|
status = MODE_SAVE;
|
|
newline = line = msg->lin;
|
|
while(line) {
|
|
newline = line;
|
|
if((line->type & GLINE_KLUD) and not AA->Viewkludge() and not (line->kludge & GKLUD_RFC))
|
|
newline = line = DeleteLine(line);
|
|
else {
|
|
strtrimline(line->txt);
|
|
if(line->type & GLINE_HARD)
|
|
line->txt += "\n";
|
|
else
|
|
line->txt += " ";
|
|
line = line->next;
|
|
}
|
|
}
|
|
msg->lin = FirstLine(newline);
|
|
|
|
w_info(NULL);
|
|
if(position)
|
|
position--;
|
|
if(not savedirect)
|
|
status = EditMsg(mode, &position, msg);
|
|
_in_editor = NO;
|
|
w_info(LNG->Wait);
|
|
|
|
if(status != MODE_QUIT) {
|
|
line = msg->lin;
|
|
while(line) {
|
|
if(line->txt.find('\n') != line->txt.npos)
|
|
line->type = GLINE_HARD;
|
|
else if(line->next and (line->next->type & GLINE_QUOT))
|
|
line->type = GLINE_HARD;
|
|
else
|
|
line->type = 0;
|
|
strtrimline(line->txt);
|
|
if(AA->isinternet() or *msg->ito or *msg->ifrom) {
|
|
// Check for signature indicator
|
|
if(streql(line->txt.c_str(), "--")) {
|
|
line->txt = "-- ";
|
|
line->type = GLINE_HARD;
|
|
}
|
|
}
|
|
line = line->next;
|
|
}
|
|
LoadCharset("N/A", "N/A");
|
|
strcpy(stpcpy(msg->charset, CFG->xlatlocalset), " 2");
|
|
msg->charsetlevel = 2;
|
|
msg->LinesToText();
|
|
}
|
|
w_info(NULL);
|
|
}
|
|
if(status == MODE_SAVE) {
|
|
|
|
if(*EDIT->External() and not EDIT->Internal())
|
|
LoadText(msg, AddPath(CFG->goldpath, EDIT->File()));
|
|
if(mode == MODE_FORWARD)
|
|
msg->attr.pos1();
|
|
int adat_viewhidden = AA->Viewhidden();
|
|
int adat_viewkludge = AA->Viewkludge();
|
|
int adat_viewquote = AA->Viewquote();
|
|
AA->adat->viewhidden = true;
|
|
AA->adat->viewkludge = true;
|
|
AA->adat->viewquote = true;
|
|
msg->TextToLines(CFG->dispmargin-1, false); // Ignore any kludge address found
|
|
AA->adat->viewhidden = adat_viewhidden;
|
|
AA->adat->viewkludge = adat_viewkludge;
|
|
AA->adat->viewquote = adat_viewquote;
|
|
msg->attr.pos0();
|
|
|
|
if(not savedirect) {
|
|
HeaderView->Use(AA, msg);
|
|
HeaderView->Paint();
|
|
BodyView->Use(AA, msg, topline);
|
|
BodyView->Paint();
|
|
do {
|
|
if(gkbd.quitall)
|
|
status = MODE_SAVE;
|
|
else {
|
|
GMenuEditfile MenuEditfile;
|
|
status = MenuEditfile.Run(msg);
|
|
}
|
|
if(status == MODE_VIEW) {
|
|
BodyView->Use(AA, msg, topline);
|
|
BodyView->Paint();
|
|
ViewMessage();
|
|
}
|
|
} while(status == MODE_VIEW);
|
|
}
|
|
|
|
InvalidateControlInfo(msg);
|
|
|
|
if(status == MODE_SAVE)
|
|
DoCrosspost(msg, post_xparea);
|
|
}
|
|
loop++;
|
|
if(status == MODE_CHANGE) {
|
|
_in_editor = YES;
|
|
HeaderView->Use(AA, msg);
|
|
HeaderView->Paint();
|
|
}
|
|
} while(status == MODE_CHANGE);
|
|
}
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
static void GetLastLink(GMsg* msg, ulong& msgno) {
|
|
|
|
GMsg* uplink = (GMsg*)throw_calloc(1, sizeof(GMsg));
|
|
|
|
uplink->msgno = msg->msgno;
|
|
uplink->link.first_set(msg->link.first());
|
|
|
|
if(uplink->link.first()) {
|
|
while(AA->Msgn.ToReln(uplink->link.first())) {
|
|
if(not AA->LoadHdr(uplink, uplink->link.first(), false))
|
|
uplink->msgno = 0;
|
|
}
|
|
}
|
|
|
|
// Return msgno of the last link
|
|
msgno = uplink->msgno;
|
|
|
|
ResetMsg(uplink);
|
|
throw_free(uplink);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
void MakeMsg(int mode, GMsg* omsg, bool ignore_replyto) {
|
|
|
|
GFTRK("MakeMsg");
|
|
|
|
// Tell GMsgHeaderView not to show msg size
|
|
_in_editor = YES;
|
|
|
|
// Allocate some msgs
|
|
GMsg* msg = (GMsg*)throw_calloc(1, sizeof(GMsg));
|
|
GMsg* reply = (GMsg*)throw_calloc(1, sizeof(GMsg));
|
|
GMsg* cmpmsg = (GMsg*)throw_calloc(1, sizeof(GMsg));
|
|
ulong reply_msgno = 0;
|
|
|
|
// Keep copy of original aka for later restoration
|
|
Addr origaka = AL.AreaIdToPtr(OrigArea)->aka();
|
|
|
|
// Force template choice if necessary
|
|
if(AA->Forcetemplate() and (mode != MODE_CHANGE))
|
|
ChangeTemplate();
|
|
|
|
// Random System
|
|
AA->RandomizeData();
|
|
|
|
// Prepare confirmation receipts
|
|
int confirm = NO;
|
|
if(mode == MODE_CONFIRM) {
|
|
confirm = YES;
|
|
mode = MODE_REPLY;
|
|
}
|
|
|
|
// Prepare crossposting
|
|
int crosspost = NO;
|
|
post_xparea.clear();
|
|
|
|
do {
|
|
if(post_xparea.size()) {
|
|
mode = MODE_COPY;
|
|
AL.SetActiveAreaNo(post_xparea.back());
|
|
if(CurrArea != OrigArea) {
|
|
AA->Open();
|
|
AA->RandomizeData(mode);
|
|
}
|
|
|
|
msg->TextToLines(CFG->dispmargin-1);
|
|
msg->orig = AA->Aka().addr;
|
|
msg->charsetlevel = LoadCharset(CFG->xlatlocalset, AA->Xlatexport());
|
|
if(msg->charsetlevel)
|
|
sprintf(msg->charset, "%s %d", AA->Xlatexport(), msg->charsetlevel);
|
|
else
|
|
sprintf(msg->charset, "%s 2", CFG->xlatlocalset);
|
|
strcpy(msg->odom, CFG->aka[AkaMatch(&msg->orig, &AA->Aka().addr)].domain);
|
|
if(AA->isecho() or have_origin(msg))
|
|
DoTearorig(mode, msg);
|
|
DoKludges(mode, msg);
|
|
msg->LinesToText();
|
|
post_xparea.pop_back();
|
|
}
|
|
|
|
int status = MODE_SAVE;
|
|
int hardlines = EDIT->HardLines();
|
|
int topline = 0;
|
|
fspecs = specfiles = 0;
|
|
fspec = (FileSpec*)throw_realloc(fspec, sizeof(FileSpec));
|
|
memset(fspec, 0, sizeof(FileSpec));
|
|
|
|
// Get msg number and reset/copy msg data
|
|
if(mode != MODE_COPY)
|
|
ResetMsg(msg);
|
|
switch(mode) {
|
|
case MODE_FORWARD:
|
|
memcpy(msg, omsg, sizeof(GMsg));
|
|
msg->txt = (char*)throw_strdup(msg->txt);
|
|
msg->lin = NULL;
|
|
msg->line = NULL;
|
|
msg->messageid = NULL;
|
|
msg->inreplyto = NULL;
|
|
msg->references = NULL;
|
|
msg->TextToLines(CFG->dispmargin-1, true, false);
|
|
msg->msgid.reset();
|
|
*msg->iorig = NUL;
|
|
*msg->idest = NUL;
|
|
*msg->ireplyto = NUL;
|
|
*msg->iaddr = NUL;
|
|
*msg->igate = NUL;
|
|
*msg->ifrom = NUL;
|
|
*msg->ito = NUL;
|
|
*msg->organization = NUL;
|
|
*msg->realby = NUL;
|
|
*msg->realto = NUL;
|
|
*msg->origin = NUL;
|
|
// Drop through ...
|
|
case MODE_COPY:
|
|
AA->NewMsgno(msg);
|
|
msg->link.reset();
|
|
break;
|
|
case MODE_CHANGE:
|
|
memcpy(msg, omsg, sizeof(GMsg));
|
|
msg->txt = (char*)throw_strdup(msg->txt);
|
|
msg->lin = NULL;
|
|
msg->line = NULL;
|
|
msg->messageid = NULL;
|
|
msg->inreplyto = NULL;
|
|
msg->references = NULL;
|
|
msg->TextToLines(CFG->dispmargin-1, true, false);
|
|
break;
|
|
case MODE_NEW:
|
|
wfill(MINROW, 0, MAXROW-2, MAXCOL-1, ' ', C_READW);
|
|
case MODE_QUOTE:
|
|
case MODE_REPLY:
|
|
case MODE_REPLYCOMMENT:
|
|
AA->NewMsgno(msg);
|
|
msg->timesread = 1;
|
|
}
|
|
|
|
msg->oorig = msg->orig;
|
|
msg->board = AA->board();
|
|
msg->odest = msg->dest;
|
|
|
|
if(mode != MODE_COPY) {
|
|
|
|
// Set msg date and time
|
|
bool dochgdate = true;
|
|
if(mode == MODE_CHANGE) {
|
|
dochgdate = EDIT->ChangeDate() ? true : false;
|
|
if((EDIT->ChangeDate() == YES) and not msg->attr.fmu())
|
|
dochgdate = false;
|
|
}
|
|
if(dochgdate) {
|
|
time_t a = time(NULL);
|
|
struct tm *tp = gmtime(&a);
|
|
tp->tm_isdst = -1;
|
|
time_t b = mktime(tp);
|
|
a += a - b;
|
|
msg->received = msg->arrived = msg->written = a;
|
|
}
|
|
|
|
// Prepare msg header
|
|
|
|
if(mode != MODE_CHANGE)
|
|
msg->attr = AA->Attributes(); // Set default attributes
|
|
|
|
switch(mode) {
|
|
case MODE_QUOTE:
|
|
case MODE_REPLYCOMMENT:
|
|
if(CurrArea != OrigArea)
|
|
AA->SetXlatimport(AL.AreaIdToPtr(OrigArea)->Xlatimport());
|
|
omsg->TextToLines(-CFG->quotemargin, true, false);
|
|
if(ignore_replyto)
|
|
*omsg->ireplyto = NUL;
|
|
if(omsg->attr.rot())
|
|
Rot13(omsg);
|
|
// Drop through
|
|
case MODE_REPLY:
|
|
if(confirm)
|
|
msg->attr = CFG->attribscfm;
|
|
if(omsg->attr.pvt() & !AA->isecho())
|
|
msg->attr.pvt1(); // Set Pvt attr in response to Pvt in orig
|
|
if(omsg->attr.frq() and (mode == MODE_REPLY) and (not confirm)) {
|
|
msg->attr = CFG->attribsfrq;
|
|
msg->attr.frq1(); // Special case for the file request function
|
|
}
|
|
|
|
// Change header data if replying to a forwarded message
|
|
if((mode == MODE_REPLYCOMMENT) and omsg->fwdorig.net) {
|
|
strcpy(omsg->by, omsg->fwdfrom);
|
|
strcpy(omsg->realby, omsg->fwdfrom);
|
|
omsg->oorig = omsg->orig = omsg->fwdorig;
|
|
strcpy(omsg->to, omsg->fwdto);
|
|
strcpy(omsg->realto, omsg->fwdto);
|
|
omsg->odest = omsg->dest = omsg->fwddest;
|
|
strcpy(omsg->re, omsg->fwdsubj);
|
|
*omsg->ireplyto = *omsg->iorig = NUL;
|
|
}
|
|
|
|
// Do aka matching
|
|
if(AA->Akamatching()) {
|
|
// ... but only if we did NOT change aka manually
|
|
if(AA->Aka().addr.equals(AA->aka())) {
|
|
Addr aka_addr = AA->Aka().addr;
|
|
AkaMatch(&aka_addr, &omsg->orig);
|
|
AA->SetAka(aka_addr);
|
|
}
|
|
}
|
|
|
|
if((mode == MODE_REPLYCOMMENT) and not omsg->fwdorig.net) {
|
|
strcpy(msg->to, omsg->to);
|
|
strcpy(msg->realto, omsg->realto);
|
|
msg->dest = omsg->dest;
|
|
strcpy(msg->idest, omsg->idest);
|
|
}
|
|
else {
|
|
strcpy(msg->to, omsg->By());
|
|
strcpy(msg->realto, omsg->realby);
|
|
msg->dest = omsg->orig;
|
|
strcpy(msg->idest, *omsg->ireplyto ? omsg->ireplyto : *omsg->iorig ? omsg->iorig : omsg->iaddr);
|
|
}
|
|
if(not *msg->iaddr)
|
|
strcpy(msg->iaddr, msg->idest);
|
|
strcpy(msg->re, omsg->re);
|
|
|
|
if(AA->Replyre() or AA->isinternet()) {
|
|
|
|
ulong number;
|
|
const char* r = get_subject_re_info(msg->re, number);
|
|
if(r) {
|
|
if(AA->Replyre() == REPLYRE_NUMERIC and not AA->isinternet()) {
|
|
ISub subj;
|
|
sprintf(subj, "Re^%lu:%s", ((number+1) > number) ? (number+1) : number, r);
|
|
strcpy(msg->re, subj);
|
|
}
|
|
}
|
|
else if(not msg->attr.frq() and not msg->attr.urq()) {
|
|
memmove(msg->re+4, msg->re, sizeof(ISub)-5);
|
|
memmove(msg->re, "Re: ", 4);
|
|
}
|
|
}
|
|
else {
|
|
char* r;
|
|
ulong number;
|
|
char* ptr = msg->re;
|
|
do {
|
|
r = (char*)get_subject_re_info(ptr, number);
|
|
if(r)
|
|
ptr = strskip_wht(r);
|
|
} while(r);
|
|
strcpy(msg->re, ptr);
|
|
}
|
|
strcpy(msg->ddom, omsg->odom);
|
|
if((mode == MODE_REPLYCOMMENT) and omsg->fwdorig.net)
|
|
strcpy(msg->replys, omsg->fwdmsgid);
|
|
else
|
|
strcpy(msg->replys, omsg->msgids);
|
|
if(AA->isnet()) {
|
|
if(omsg->messageid) {
|
|
throw_free(msg->inreplyto);
|
|
msg->inreplyto = throw_strdup(omsg->messageid);
|
|
}
|
|
strcpy(msg->igate, omsg->igate);
|
|
if(*msg->igate) {
|
|
msg->dest.set(msg->igate);
|
|
char* ptr = strchr(msg->igate, ' ');
|
|
if(ptr) {
|
|
if(not isuucp(msg->to))
|
|
strcpy(msg->realto, msg->to);
|
|
strcpy(msg->to, strskip_wht(ptr));
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
int reflen = omsg->references ? strlen(omsg->references) : 0;
|
|
int midlen = omsg->messageid ? strlen(omsg->messageid) : 0;
|
|
msg->references = (char*)throw_calloc(1, reflen+1+midlen+1);
|
|
if(omsg->references)
|
|
strcpy(msg->references, omsg->references);
|
|
if(omsg->messageid) {
|
|
if(reflen)
|
|
strcat(msg->references, " ");
|
|
strcat(msg->references, omsg->messageid);
|
|
}
|
|
if(*msg->references == NUL)
|
|
throw_release(msg->references);
|
|
}
|
|
if(CurrArea == OrigArea) {
|
|
if((CFG->replylink == REPLYLINK_DIRECT) or AA->isjam())
|
|
reply_msgno = omsg->msgno;
|
|
else if(CFG->replylink == REPLYLINK_CHAIN)
|
|
GetLastLink(omsg, reply_msgno);
|
|
msg->link.to_set(reply_msgno);
|
|
if(msg->link.to() == omsg->msgno)
|
|
omsg->link.first_set(msg->msgno);
|
|
}
|
|
|
|
///////////
|
|
/////////// Drop through
|
|
///////////
|
|
|
|
case MODE_FORWARD:
|
|
if(mode == MODE_FORWARD) {
|
|
if(msg->fwdorig.net == 0) {
|
|
strcpy(msg->fwdfrom, msg->By());
|
|
msg->fwdorig = msg->orig;
|
|
strcpy(msg->fwdto, msg->To());
|
|
msg->fwddest = msg->dest;
|
|
strxcpy(msg->fwdsubj, msg->re, sizeof(msg->fwdsubj));
|
|
Area* fwdarea = AL.AreaIdToPtr(OrigArea);
|
|
strcpy(msg->fwdarea, fwdarea->isecho() ? fwdarea->echoid() : "");
|
|
strcpy(msg->fwdmsgid, msg->msgids);
|
|
}
|
|
}
|
|
|
|
///////////
|
|
/////////// Drop through
|
|
///////////
|
|
|
|
case MODE_NEW:
|
|
strcpy(msg->by, AA->Username().name);
|
|
msg->attr.nwm1();
|
|
if(not AA->islocal())
|
|
msg->attr.uns1();
|
|
strcpy(msg->odom, CFG->aka[AkaMatch(&msg->orig, &AA->Aka().addr)].domain);
|
|
break;
|
|
|
|
case MODE_CHANGE:
|
|
if(not AA->islocal())
|
|
msg->attr.uns1();
|
|
msg->attr.snt0();
|
|
msg->attr.rcv0();
|
|
break;
|
|
}
|
|
|
|
// Get random items
|
|
|
|
switch(mode) {
|
|
case MODE_NEW:
|
|
case MODE_FORWARD:
|
|
*msg->replys = NUL;
|
|
throw_release(msg->references);
|
|
throw_release(msg->inreplyto);
|
|
if(AA->isnet()) {
|
|
strcpy(msg->to, "");
|
|
msg->dest.reset_fast();
|
|
msg->odest.reset_fast();
|
|
}
|
|
if(not AA->isnet() or (AA->isemail() and strchr(AA->Whoto(), '@')))
|
|
strcpy(msg->to, AA->Whoto());
|
|
|
|
///////
|
|
/////// Drop through
|
|
///////
|
|
|
|
case MODE_REPLYCOMMENT:
|
|
case MODE_QUOTE:
|
|
case MODE_REPLY:
|
|
case MODE_CHANGE:
|
|
AA->RandomizeData(mode);
|
|
if(mode != MODE_CHANGE) {
|
|
msg->orig = AA->Aka().addr;
|
|
if(AA->isinternet())
|
|
strcpy(msg->iorig, AA->Internetaddress());
|
|
if(not strblank(AA->Username().name))
|
|
strcpy(msg->by, AA->Username().name);
|
|
if(CFG->switches.get(emptytearline))
|
|
*msg->tearline = NUL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
int forwstat = NO;
|
|
if(msg->odest.net == 0)
|
|
msg->odest = msg->dest;
|
|
status = NO;
|
|
if(not confirm) {
|
|
strcpy(cmpmsg->by, msg->by);
|
|
strcpy(cmpmsg->to, msg->to);
|
|
strcpy(cmpmsg->re, msg->re);
|
|
cmpmsg->attr = msg->attr;
|
|
if(savedirect) {
|
|
CheckSubject(msg, msg->re);
|
|
}
|
|
else {
|
|
GMenuEditHeader MenuEditHeader;
|
|
while((status = MenuEditHeader.Run(mode, msg)) == YES);
|
|
}
|
|
}
|
|
|
|
if(status == NO) {
|
|
if(mode == MODE_CHANGE or mode == MODE_FORWARD) {
|
|
status = MODE_SAVE;
|
|
if(mode == MODE_FORWARD)
|
|
forwstat = YES;
|
|
}
|
|
else {
|
|
if(confirm) {
|
|
throw_release(msg->txt);
|
|
FILE* fp = fsopen(AddPath(CFG->goldpath, CFG->confirmfile), "rt", CFG->sharemode);
|
|
if(fp) {
|
|
LoadText(msg, AddPath(CFG->goldpath, CFG->confirmfile));
|
|
fclose(fp);
|
|
}
|
|
if(msg->txt == NULL)
|
|
msg->txt = throw_strdup("\r\rConfirmation Receipt\r\r");
|
|
TokenXlat(mode, msg->txt, msg, omsg, CurrArea);
|
|
}
|
|
else
|
|
CreateFileAddr(msg);
|
|
msg->TextToLines(CFG->dispmargin-1);
|
|
status = MODE_SAVE;
|
|
}
|
|
}
|
|
MakeMsg2(mode, status, forwstat, topline, msg, omsg, cmpmsg);
|
|
}
|
|
|
|
if(status != MODE_SAVE) {
|
|
post_xparea.clear();
|
|
crosspost = NO;
|
|
}
|
|
|
|
if(status == MODE_SAVE) {
|
|
|
|
if(*msg->iorig and (strlen(msg->iorig) < sizeof(Name))) {
|
|
strcpy(msg->realby, msg->by);
|
|
strcpy(msg->by, msg->iorig);
|
|
}
|
|
|
|
if(mode == MODE_COPY)
|
|
AA->SaveMsg(GMSG_NEW, msg);
|
|
else
|
|
MakeMsg3(mode, msg);
|
|
|
|
// If message is a reply, update the links on the original
|
|
if(CurrArea == OrigArea and (mode == MODE_QUOTE or mode == MODE_REPLYCOMMENT or mode == MODE_REPLY)) {
|
|
if(AA->Msgn.ToReln(reply_msgno)) {
|
|
if(AA->LoadHdr(reply, reply_msgno, false)) {
|
|
ulong replynext;
|
|
bool ok2save = false;
|
|
if(AA->issquish()) {
|
|
if(reply->link.first()) {
|
|
for(int r=0; r<reply->link.list_max()-1; r++) {
|
|
if(reply->link.list(r) == 0) {
|
|
reply->link.list_set(r, msg->msgno);
|
|
ok2save = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
reply->link.first_set(msg->msgno);
|
|
ok2save = true;
|
|
}
|
|
}
|
|
else if(AA->isjam()) {
|
|
if(reply->link.first()) {
|
|
replynext = reply->link.first();
|
|
do {
|
|
reply_msgno = replynext;
|
|
if(not AA->LoadHdr(reply, reply_msgno, false))
|
|
break;
|
|
replynext = reply->link.next();
|
|
} while(reply->link.next());
|
|
if(reply->link.next() == 0) {
|
|
reply->link.next_set(msg->msgno);
|
|
ok2save = true;
|
|
}
|
|
}
|
|
else {
|
|
reply->link.first_set(msg->msgno);
|
|
ok2save = true;
|
|
}
|
|
}
|
|
else {
|
|
reply->link.first_set(msg->msgno);
|
|
ok2save = true;
|
|
}
|
|
if(ok2save) {
|
|
reply->pcboard.reply_written = msg->written;
|
|
AA->SaveHdr(GMSG_UPDATE, reply);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ask if the original msg should be deleted while we're at it
|
|
if(CurrArea == OrigArea and CFG->switches.get(askdelorig) and not confirm) {
|
|
switch(mode) {
|
|
case MODE_REPLY:
|
|
case MODE_QUOTE:
|
|
case MODE_REPLYCOMMENT:
|
|
if(not AA->isecho()) {
|
|
topline = 0;
|
|
omsg->TextToLines(CFG->dispmargin-1);
|
|
if(omsg->attr.rot())
|
|
Rot13(omsg);
|
|
HeaderView->Use(AA, omsg);
|
|
HeaderView->Paint();
|
|
BodyView->Use(AA, omsg, topline);
|
|
BodyView->Paint();
|
|
GMenuDelorig MenuDelorig;
|
|
if(MenuDelorig.Run()) {
|
|
AA->Mark.Del(omsg->msgno);
|
|
AA->PMrk.Del(omsg->msgno);
|
|
AA->DeleteMsg(omsg, DIR_PREV);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(CurrArea != OrigArea) {
|
|
AA->UpdateAreadata();
|
|
AA->Close();
|
|
AL.SetActiveAreaId(OrigArea);
|
|
AA->RandomizeData();
|
|
}
|
|
AA->UpdateAreadata();
|
|
EDIT->HardLines(hardlines);
|
|
for(int n=0; n<fspecs; n++)
|
|
throw_release(fspec[n].fblk);
|
|
fspecs = specfiles = 0;
|
|
throw_release(fspec);
|
|
|
|
if(crosspost and post_xparea.size())
|
|
update_statuslinef(LNG->Crossposting, AL[post_xparea.back()]->echoid());
|
|
} while(post_xparea.size());
|
|
|
|
_in_editor = NO;
|
|
|
|
// Restore original aka
|
|
AA->SetAka(origaka);
|
|
|
|
ResetMsg(omsg);
|
|
ResetMsg(cmpmsg);
|
|
ResetMsg(reply);
|
|
ResetMsg(msg);
|
|
|
|
throw_free(cmpmsg);
|
|
throw_free(reply);
|
|
throw_free(msg);
|
|
|
|
LoadLanguage(AA->Loadlanguage());
|
|
|
|
GFTRK(NULL);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|