/* Copyright 2001-2018 John Wiseman G8BPQ This file is part of LinBPQ/BPQ32. LinBPQ/BPQ32 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 3 of the License, or (at your option) any later version. LinBPQ/BPQ32 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 LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses */ #define _CRT_SECURE_NO_DEPRECATE #include "CHeaders.h" #include "bpqmail.h" #define MAIL #include "httpconnectioninfo.h" #ifdef WIN32 //#include "C:\Program Files (x86)\GnuWin32\include\iconv.h" #else #include #include #endif static struct HTTPConnectionInfo * FindSession(char * Key); int APIENTRY SessionControl(int stream, int command, int param); int SetupNodeMenu(char * Buff); VOID SetMultiStringValue(char ** values, char * Multi); char * GetTemplateFromFile(int Version, char * FN); VOID FormatTime(char * Time, time_t cTime); struct MsgInfo * GetMsgFromNumber(int msgno); BOOL CheckUserMsg(struct MsgInfo * Msg, char * Call, BOOL SYSOP); BOOL OkToKillMessage(BOOL SYSOP, char * Call, struct MsgInfo * Msg); int DisplayWebForm(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, char * FileName, char * XML, char * Reply, char * RawMessage, int RawLen); struct HTTPConnectionInfo * AllocateWebMailSession(); VOID SaveNewMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest, int InputLen); void ConvertTitletoUTF8(WebMailInfo * WebMail, char * Title, char * UTF8Title, int Len); char *stristr (char *ch1, char *ch2); char * ReadTemplate(char * FormSet, char * DirName, char * FileName); VOID DoStandardTemplateSubsitutions(struct HTTPConnectionInfo * Session, char * txtFile); BOOL CheckifPacket(char * Via); int GetHTMLFormSet(char * FormSet); void ProcessFormInput(struct HTTPConnectionInfo * Session, char * input, char * Reply, int * RLen, int InputLen); char * WebFindPart(char ** Msg, char * Boundary, int * PartLen, char * End); struct HTTPConnectionInfo * FindWMSession(char * Key); int SendWebMailHeaderEx(char * Reply, char * Key, struct HTTPConnectionInfo * Session, char * Alert); char * BuildFormMessage(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, char * Keys[1000], char * Values[1000], int NumKeys); char * FindXMLVariable(WebMailInfo * WebMail, char * Var); int ReplyToFormsMessage(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, char * Reply, BOOL Reenter); BOOL ParsetxtTemplate(struct HTTPConnectionInfo * Session, struct HtmlFormDir * Dir, char * FN, BOOL isReply); VOID UpdateFormAction(char * Template, char * Key); BOOL APIENTRY GetAPRSLatLon(double * PLat, double * PLon); BOOL APIENTRY GetAPRSLatLonString(char * PLat, char * PLon); void FreeWebMailFields(WebMailInfo * WebMail); VOID BuildXMLAttachment(struct HTTPConnectionInfo * Session, char * Keys[1000], char * Values[1000], int NumKeys); VOID SaveTemplateMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest); VOID DownloadAttachments(struct HTTPConnectionInfo * Session, char * Reply, int * RLen, char * Rest); VOID getAttachmentList(struct HTTPConnectionInfo * Session, char * Reply, int * RLen, char * Rest); char * BuildB2Header(WebMailInfo * WebMail, struct MsgInfo * Msg, char ** ToCalls, int Calls); VOID FormatTime2(char * Time, time_t cTime); VOID ProcessSelectResponse(struct HTTPConnectionInfo * Session, char * URLParams); VOID ProcessAskResponse(struct HTTPConnectionInfo * Session, char * URLParams); char * CheckFile(struct HtmlFormDir * Dir, char * FN); VOID GetPage(struct HTTPConnectionInfo * Session, char * NodeURL); VOID SendTemplateSelectScreen(struct HTTPConnectionInfo * Session, char *URLParams, int InputLen); BOOL isAMPRMsg(char * Addr); char * doXMLTransparency(char * string); Dll BOOL APIENTRY APISendAPRSMessage(char * Text, char * ToCall); void SendMessageReadEvent(char * Call, struct MsgInfo * Msg); void SendNewMessageEvent(char * call, struct MsgInfo * Msg); void MQTTMessageEvent(void* message); extern char NodeTail[]; extern char BBSName[10]; extern char LTFROMString[2048]; extern char LTTOString[2048]; extern char LTATString[2048]; UCHAR BPQDirectory[260]; int LineCount = 35; // Lines per page on message list // Forms struct HtmlFormDir ** HtmlFormDirs = NULL; int FormDirCount = 0; struct HtmlForm { char * FileName; BOOL HasInitial; BOOL HasViewer; BOOL HasReply; BOOL HasReplyViewer; }; struct HtmlFormDir { char * FormSet; char * DirName; struct HtmlForm ** Forms; int FormCount; struct HtmlFormDir ** Dirs; // Nested Directories int DirCount; }; char FormDirList[4][MAX_PATH] = {"Standard_Templates", "Standard Templates", "Local_Templates"}; static char PassError[] = "

Sorry, User or Password is invalid - please try again

"; static char BusyError[] = "

Sorry, No sessions available - please try later

"; extern char MailSignon[]; char WebMailSignon[] = "BPQ32 Mail Server Access" "

BPQ32 Mail Server %s Access

" "

Please enter Callsign and Password to access WebMail

" "
" "" "" "
User
Password
" "

"; static char MsgInputPage[] = "" "" "" "" "" "

Webmail Interface - Message Input Form

" "
" "
" "To      %s
" "Subject      " // "" // "
" "" "
Type    " "" " BID
" "" "" "
" "
" "
"; static char CheckFormMsgPage[] = "" "" "" "

Webmail Forms Interface - Check Message

" "
" "
" "To      
" "CC      
" "Subject " //"" "
Type    " "" " BID

" "
" "
" "
"; extern char * WebMailTemplate; extern char * WebMailMsgTemplate; extern char * jsTemplate; static char *dat[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; char *longday[] = {"Sunday", "Monday", "Tusday", "Wednesday", "Thusday", "Friday", "Saturday"}; static struct HTTPConnectionInfo * WebSessionList = NULL; // active WebMail sessions #ifdef LINBPQ UCHAR * GetBPQDirectory(); #endif void UndoTransparency(char * input); #ifndef LINBPQ void UndoTransparency(char * input) { char * ptr1, * ptr2; char c; int hex; if (input == NULL) return; ptr1 = ptr2 = input; // Convert any %xx constructs while (1) { c = *(ptr1++); if (c == 0) break; if (c == '%') { c = *(ptr1++); if(isdigit(c)) hex = (c - '0') << 4; else hex = (tolower(c) - 'a' + 10) << 4; c = *(ptr1++); if(isdigit(c)) hex += (c - '0'); else hex += (tolower(c) - 'a' + 10); *(ptr2++) = hex; } else if (c == '+') *(ptr2++) = 32; else *(ptr2++) = c; } *ptr2 = 0; } #endif void ReleaseWebMailStruct(WebMailInfo * WebMail) { // release any malloc'ed resources if (WebMail == NULL) return; FreeWebMailFields(WebMail); free(WebMail); return; } VOID FreeWebMailMallocs() { // called when closing. Not really needed, but simplifies tracking down real memory leaks struct HTTPConnectionInfo * Session, * SaveNext; int i; Session = WebSessionList; while (Session) { SaveNext = Session->Next; // Release amy malloc'ed resouces ReleaseWebMailStruct(Session->WebMail); free(Session); Session = SaveNext; } for (i = 0; i < FormDirCount; i++) { struct HtmlFormDir * Dir = HtmlFormDirs[i]; int j; for (j = 0; j < Dir->FormCount; j++) { free(Dir->Forms[j]->FileName); free(Dir->Forms[j]); } if (Dir->DirCount) { struct HtmlFormDir * SubDir; int k, l; for (l = 0; l < Dir->DirCount; l++) { SubDir = Dir->Dirs[l]; for (k = 0; k < Dir->Dirs[l]->FormCount; k++) { free(SubDir->Forms[k]->FileName); free(SubDir->Forms[k]); } free(SubDir->DirName); free(SubDir->Forms); free(SubDir->FormSet); free(Dir->Dirs[l]); } } free(Dir->DirName); free(Dir->Forms); free(Dir->FormSet); free(Dir); } free(HtmlFormDirs); return; } char * initMultipartUnpack(char ** Input) { // Check if Multipart and return Boundary. Update Input to first part // look through header for Content-Type line, and if multipart // find boundary string. char * ptr, * ptr2; char Boundary[128]; BOOL Multipart = FALSE; ptr = *Input; while(*ptr != 13) { ptr2 = strchr(ptr, 10); // Find CR while(ptr2[1] == ' ' || ptr2[1] == 9) // Whitespace - continuation line ptr2 = strchr(&ptr2[1], 10); // Find CR if (_memicmp(ptr, "Content-Type: ", 14) == 0) { char Line[256] = ""; char * ptr3; size_t len = ptr2-ptr-14; if (len >255) return NULL; memcpy(Line, &ptr[14], len); if (_memicmp(Line, "Multipart/", 10) == 0) { ptr3 = stristr(Line, "boundary"); if (ptr3) { ptr3+=9; if ((*ptr3) == '"') ptr3++; strcpy(Boundary, ptr3); ptr3 = strchr(Boundary, '"'); if (ptr3) *ptr3 = 0; ptr3 = strchr(Boundary, 13); // CR if (ptr3) *ptr3 = 0; break; } else return NULL; // Can't do anything without a boundary ?? } } ptr = ptr2; ptr++; } // Find First part - there is a boundary before it ptr = strstr(ptr2, Boundary); // Next should be crlf then part ptr = strstr(ptr, "\r\n"); if (ptr) ptr += 2; // Over CRLF *Input = ptr; // Return first part or NULL return _strdup(Boundary); } BOOL unpackPart(char * Boundary, char ** Input, char ** Name, char ** Value, int * ValLen, char * End) { // Format seems to be /* ------WebKitFormBoundaryABJaEbBWB5SuAHmq Content-Disposition: form-data; name="Subj" subj ------WebKitFormBoundaryABJaEbBWB5SuAHmq Content-Disposition: form-data; name="myFile[]"; filename="exiftool.txt" Content-Type: text/plain c:\exiftool "-filenameTo = _strdup(Value); else if (strcmp(Name, "CC") == 0) WebMail->CC = _strdup(Value); else if (strcmp(Name, "Subj") == 0) WebMail->Subject = _strdup(Value); else if (strcmp(Name, "Type") == 0) WebMail->Type = Value[0]; else if (strcmp(Name, "BID") == 0) WebMail->BID = _strdup(Value); else if (strcmp(Name, "Msg") == 0) WebMail->Body = _strdup(Value); else if (_memicmp(Name, "myFile[]", 8) == 0) { // Get File Name from param string - myFile[]"; filename="exiftool.txt" \r\nContent-Type: text/plain char * fn = strstr(Name, "filename="); char * endfn; if (fn) { fn += 10; endfn = strchr(fn, '"'); if (endfn) { *endfn = 0; if (strlen(fn)) { WebMail->FileName[WebMail->Files] = _strdup(fn); WebMail->FileBody[WebMail->Files] = malloc(ValLength); memcpy(WebMail->FileBody[WebMail->Files], Value, ValLength); WebMail->FileLen[WebMail->Files++] = ValLength; } } } } else if (_memicmp(Name, "myFile2[]", 8) == 0) { // Get File Name from param string - myFile[]"; filename="exiftool.txt" \r\nContent-Type: text/plain char * fn = strstr(Name, "filename="); char * endfn; if (fn) { fn += 10; endfn = strchr(fn, '"'); if (endfn) { *endfn = 0; if (strlen(fn)) { WebMail->Header = malloc(ValLength + 1); memcpy(WebMail->Header, Value, ValLength + 1); WebMail->HeaderLen = RemoveLF(WebMail->Header, ValLength); } } } } else if (_memicmp(Name, "myFile3[]", 8) == 0) { // Get File Name from param string - myFile[]"; filename="exiftool.txt" \r\nContent-Type: text/plain char * fn = strstr(Name, "filename="); char * endfn; if (fn) { fn += 10; endfn = strchr(fn, '"'); if (endfn) { *endfn = 0; if (strlen(fn)) { WebMail->Footer = malloc(ValLength + 1); memcpy(WebMail->Footer, Value, ValLength + 1); WebMail->FooterLen = RemoveLF(WebMail->Footer, ValLength); } } } } return TRUE; } struct HTTPConnectionInfo * AllocateWebMailSession() { int KeyVal; struct HTTPConnectionInfo * Session, * SaveNext; time_t NOW = time(NULL); // First see if any session records havent been used for a while Session = WebSessionList; while (Session) { if (NOW - Session->WebMailLastUsed > 1200) // 20 Mins { SaveNext = Session->Next; // Release amy malloc'ed resouces ReleaseWebMailStruct(Session->WebMail); memset(Session, 0, sizeof(struct HTTPConnectionInfo)); Session->Next = SaveNext; goto UseThis; } Session = Session->Next; } Session = zalloc(sizeof(struct HTTPConnectionInfo)); if (Session == NULL) return NULL; if (WebSessionList) Session->Next = WebSessionList; WebSessionList = Session; UseThis: Session->WebMail = zalloc(sizeof(WebMailInfo)); KeyVal = ((rand() % 100) + 1); KeyVal *= (int)time(NULL); sprintf(Session->Key, "%c%08X", 'W', KeyVal); return Session; } struct HTTPConnectionInfo * FindWMSession(char * Key) { struct HTTPConnectionInfo * Session = WebSessionList; while (Session) { if (strcmp(Session->Key, Key) == 0) { Session->WebMailLastUsed = time(NULL); return Session; } Session = Session->Next; } return NULL; } // Build list of available forms VOID ProcessFormDir(char * FormSet, char * DirName, struct HtmlFormDir *** xxx, int * DirCount) { struct HtmlFormDir * FormDir; struct HtmlFormDir ** FormDirs = *xxx; struct HtmlForm * Form; char Search[MAX_PATH]; int count = *DirCount; #ifdef WIN32 HANDLE hFind = INVALID_HANDLE_VALUE; WIN32_FIND_DATA ffd; #else DIR *dir; struct dirent *entry; char name[256]; #endif FormDir = zalloc(sizeof (struct HtmlFormDir)); FormDir->DirName = _strdup(DirName); FormDir->FormSet = _strdup(FormSet); FormDirs=realloc(FormDirs, (count + 1) * sizeof(void *)); FormDirs[count++] = FormDir; *DirCount = count; *xxx = FormDirs; // Scan Directory for .txt files sprintf(Search, "%s/%s/%s/*", GetBPQDirectory(), FormSet, DirName); // Find the first file in the directory. #ifdef WIN32 hFind = FindFirstFile(Search, &ffd); if (INVALID_HANDLE_VALUE == hFind) return; do { if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { char Dir[MAX_PATH]; if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) continue; // Recurse in subdir sprintf(Dir, "%s/%s", DirName, ffd.cFileName); ProcessFormDir(FormSet, Dir, &FormDir->Dirs, &FormDir->DirCount); continue; } // Add to list Form = zalloc(sizeof (struct HtmlForm)); Form->FileName = _strdup(ffd.cFileName); FormDir->Forms=realloc(FormDir->Forms, (FormDir->FormCount + 1) * sizeof(void *)); FormDir->Forms[FormDir->FormCount++] = Form; } while (FindNextFile(hFind, &ffd) != 0); FindClose(hFind); #else sprintf(Search, "%s/%s/%s", GetBPQDirectory(), FormSet, DirName); if (!(dir = opendir(Search))) { Debugprintf("%s %d %d", "cant open forms dir", errno, dir); return ; } while ((entry = readdir(dir)) != NULL) { if (entry->d_type == DT_DIR) { char Dir[MAX_PATH]; if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; // Recurse in subdir sprintf(Dir, "%s/%s", DirName, entry->d_name); ProcessFormDir(FormSet, Dir, &FormDir->Dirs, &FormDir->DirCount); continue; } // Add to list Form = zalloc(sizeof (struct HtmlForm)); Form->FileName = _strdup(entry->d_name); FormDir->Forms=realloc(FormDir->Forms, (FormDir->FormCount + 1) * sizeof(void *)); FormDir->Forms[FormDir->FormCount++] = Form; } closedir(dir); #endif return; } int GetHTMLForms() { int n = 0; while (FormDirList[n][0]) GetHTMLFormSet(FormDirList[n++]); return 0; } int GetHTMLFormSet(char * FormSet) { int i; #ifdef WIN32 WIN32_FIND_DATA ffd; char szDir[MAX_PATH]; HANDLE hFind = INVALID_HANDLE_VALUE; DWORD dwError=0; sprintf(szDir, "%s/%s/*", BPQDirectory, FormSet); // Find the first file in the directory. hFind = FindFirstFile(szDir, &ffd); if (INVALID_HANDLE_VALUE == hFind) { // Accept either return 0; } // Scan all directories looking for file do { if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) continue; // Add to Directory List ProcessFormDir(FormSet, ffd.cFileName, &HtmlFormDirs, &FormDirCount); } } while (FindNextFile(hFind, &ffd) != 0); FindClose(hFind); #else DIR *dir; struct dirent *entry; char name[256]; sprintf(name, "%s/%s", BPQDirectory, FormSet); if (!(dir = opendir(name))) { Debugprintf("cant open forms dir %s %d %d", name, errno, dir); } else { while ((entry = readdir(dir)) != NULL) { if (entry->d_type == DT_DIR) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; // Add to Directory List ProcessFormDir(FormSet, entry->d_name, &HtmlFormDirs, &FormDirCount); } } closedir(dir); } #endif // List for testing return 0; Debugprintf("%d form dirs", FormDirCount); for (i = 0; i < FormDirCount; i++) { struct HtmlFormDir * Dir = HtmlFormDirs[i]; int j; Debugprintf("%3d %s", Dir->FormCount, Dir->DirName); for (j = 0; j < Dir->FormCount; j++) Debugprintf(" %s", Dir->Forms[j]->FileName); if (Dir->DirCount) { int k, l; for (l = 0; l < Dir->DirCount; l++) { Debugprintf("Subdir %3d %s", Dir->Dirs[l]->DirCount, Dir->Dirs[l]->DirName); for (k = 0; k < Dir->Dirs[l]->FormCount; k++) Debugprintf(" %s", Dir->Dirs[l]->Forms[k]->FileName); } } } return 0; } static int compare(const void *arg1, const void *arg2) { // Compare Calls. Fortunately call is at start of stuct return _stricmp(*(char**)arg1 , *(char**)arg2); } int SendWebMailHeader(char * Reply, char * Key, struct HTTPConnectionInfo * Session) { return SendWebMailHeaderEx(Reply, Key, Session, NULL); } int SendWebMailHeaderEx(char * Reply, char * Key, struct HTTPConnectionInfo * Session, char * Alert) { // Ex includes an alert string to be sent before message struct UserInfo * User = Session->User; char Messages[245000]; int m; struct MsgInfo * Msg; char * ptr = Messages; int n = NumberofMessages; //LineCount; char Via[64]; int Count = 0; Messages[0] = 0; if (Alert && Alert[0]) ptr += sprintf(Messages, "", Alert, Key); ptr += sprintf(ptr, "%s", " # Date XX Len To @ From Subject\r\n\r\n"); for (m = LatestMsg; m >= 1; m--) { if (ptr > &Messages[244000]) break; // protect buffer Msg = GetMsgFromNumber(m); if (Msg == 0 || Msg->type == 0 || Msg->status == 0) continue; // Protect against corrupt messages if (Msg && CheckUserMsg(Msg, User->Call, User->flags & F_SYSOP)) { char UTF8Title[4096]; char * EncodedTitle; // List if it is the right type and in the page range we want if (Session->WebMailTypes[0] && strchr(Session->WebMailTypes, Msg->type) == 0) continue; // All Types or right Type. Check Mine Flag if (Session->WebMailMine) { // Only list if to or from me if (strcmp(User->Call, Msg->to) != 0 && strcmp(User->Call, Msg->from) != 0) continue; } if (Session->WebMailMyTX) { // Only list if to or from me if (strcmp(User->Call, Msg->from) != 0) continue; } if (Session->WebMailMyRX) { // Only list if to or from me if (strcmp(User->Call, Msg->to)!= 0) continue; } if (Count++ < Session->WebMailSkip) continue; strcpy(Via, Msg->via); strlop(Via, '.'); // make sure title is HTML safe (no < > etc) and UTF 8 encoded EncodedTitle = doXMLTransparency(Msg->title); memset(UTF8Title, 0, 4096); // In case convert fails part way through ConvertTitletoUTF8(Session->WebMail, EncodedTitle, UTF8Title, 4095); free(EncodedTitle); ptr += sprintf(ptr, "%6d %s %c%c %5d %-8s%-8s%-8s%s\r\n", Key, Msg->number, Msg->number, FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type, Msg->status, Msg->length, Msg->to, Via, Msg->from, UTF8Title); n--; if (n == 0) break; } } if (WebMailTemplate == NULL) WebMailTemplate = GetTemplateFromFile(6, "WebMailPage.txt"); return sprintf(Reply, WebMailTemplate, BBSName, User->Call, Key, Key, Key, Key, Key, Key, Key, Key, Key, Key, Messages); } int ViewWebMailMessage(struct HTTPConnectionInfo * Session, char * Reply, int Number, BOOL DisplayHTML) { char * Key = Session->Key; struct UserInfo * User = Session->User; WebMailInfo * WebMail = Session->WebMail; char * DisplayStyle; char Message[200000] = ""; struct MsgInfo * Msg; char * ptr = Message; char * MsgBytes, * Save; int msgLen; char FullTo[100]; char UTF8Title[4096]; int Index; char * crcrptr; char DownLoad[256] = ""; DisplayStyle = "textarea"; // Prevents interpretation of html and xml Msg = GetMsgFromNumber(Number); if (Msg == NULL) { ptr += sprintf(ptr, "Message %d not found\r\n", Number); return sprintf(Reply, WebMailTemplate, BBSName, User->Call, Key, Key, Key, Key, Key, Key, Key, Message); } // New Display so free any old values FreeWebMailFields(WebMail); WebMail->CurrentMessageIndex = Number; if (!CheckUserMsg(Msg, User->Call, User->flags & F_SYSOP)) { ptr += sprintf(ptr, "Message %d not for you\r", Number); return sprintf(Reply, WebMailTemplate, BBSName, User->Call, Key, Key, Key, Key, Key, Key, Key, Message); } if (_stricmp(Msg->to, "RMS") == 0) sprintf(FullTo, "RMS:%s", Msg->via); else if (Msg->to[0] == 0) sprintf(FullTo, "smtp:%s", Msg->via); else strcpy(FullTo, Msg->to); // make sure title is UTF 8 encoded memset(UTF8Title, 0, 4096); // In case convert fails part way through ConvertTitletoUTF8(Session->WebMail, Msg->title, UTF8Title, 4095); // if a B2 message diplay B2 Header instead of a locally generated one if ((Msg->B2Flags & B2Msg) == 0) { ptr += sprintf(ptr, "From: %s%s\nTo: %s\nType/Status: %c%c\nDate/Time: %s\nBid: %s\nTitle: %s\n\n", Msg->from, Msg->emailfrom, FullTo, Msg->type, Msg->status, FormatDateAndTime((time_t)Msg->datecreated, FALSE), Msg->bid, UTF8Title); } MsgBytes = Save = ReadMessageFile(Number); msgLen = Msg->length; if (Msg->type == 'P') Index = PMSG; else if (Msg->type == 'B') Index = BMSG; else Index = TMSG; if (MsgBytes) { if (Msg->B2Flags & B2Msg) { char * ptr1; // if message has attachments, display them if plain text if (Msg->B2Flags & Attachments) { int BodyLen, NewLen; int i; char *ptr2, *attptr; sprintf(DownLoad, "Save Attachments", Key, Msg->number); WebMail->Files = 0; ptr1 = MsgBytes; // ptr += sprintf(ptr, "Message has Attachments\r\n\r\n"); while(*ptr1 != 13) { ptr2 = strchr(ptr1, 10); // Find CR if (memcmp(ptr1, "Body: ", 6) == 0) { BodyLen = atoi(&ptr1[6]); } if (memcmp(ptr1, "File: ", 6) == 0) { char * ptr3 = strchr(&ptr1[6], ' '); // Find Space *(ptr2 - 1) = 0; WebMail->FileLen[WebMail->Files] = atoi(&ptr1[6]); WebMail->FileName[WebMail->Files++] = _strdup(&ptr3[1]); *(ptr2 - 1) = ' '; // put space back } ptr1 = ptr2; ptr1++; } ptr1 += 2; // Over Blank Line and Separator // ptr1 is pointing to body. Save for possible reply WebMail->Body = malloc(BodyLen + 2); memcpy(WebMail->Body, ptr1, BodyLen); WebMail->Body[BodyLen] = 0; *(ptr1 + BodyLen) = 0; ptr += sprintf(ptr, "%s", MsgBytes); // B2 Header and Body ptr1 += BodyLen + 2; // to first file // Save pointers to file attptr = ptr1; for (i = 0; i < WebMail->Files; i++) { WebMail->FileBody[i] = malloc(WebMail->FileLen[i]); memcpy(WebMail->FileBody[i], attptr, WebMail->FileLen[i]); attptr += (WebMail->FileLen[i] + 2); } // if first (only??) attachment is XML and filename // starts "RMS_Express_Form" process as HTML Form if (DisplayHTML && _memicmp(ptr1, "FileName[0], "RMS_Express_Form_", 16) == 0) { int Len = DisplayWebForm(Session, Msg, WebMail->FileName[0], ptr1, Reply, MsgBytes, BodyLen + 32); // 32 for added "has attachments" free(MsgBytes); // Flag as read if ((_stricmp(Msg->to, User->Call) == 0) || ((User->flags & F_SYSOP) && (_stricmp(Msg->to, "SYSOP") == 0))) { if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D')) { if (Msg->status != 'Y') { Msg->status = 'Y'; Msg->datechanged=time(NULL); SaveMessageDatabase(); SendMessageReadEvent(Session->Callsign, Msg); } } } return Len; } for (i = 0; i < WebMail->Files; i++) { int n; char * p = ptr1; char c; // Check if message is probably binary int BinCount = 0; NewLen = WebMail->FileLen[i]; for (n = 0; n < NewLen; n++) { c = *p; if (c==0 || (c & 128)) BinCount++; p++; } if (BinCount > NewLen/10) { // File is probably Binary ptr += sprintf(ptr, "\rAttachment %s is a binary file\r", WebMail->FileName[i]); } else { *(ptr1 + NewLen) = 0; ptr += sprintf(ptr, "\rAttachment %s\r\r", WebMail->FileName[i]); RemoveLF(ptr1, NewLen + 1); // Removes LF after CR but not on its own ptr += sprintf(ptr, "%s\r\r", ptr1); User->Total.MsgsSent[Index] ++; User->Total.BytesForwardedOut[Index] += NewLen; } ptr1 += WebMail->FileLen[i]; ptr1 +=2; // Over separator } free(Save); ptr += sprintf(ptr, "\r\r[End of Message #%d from %s]\r", Number, Msg->from); RemoveLF(Message, (int)strlen(Message) + 1); // Removes LF after CR but not on its own if ((_stricmp(Msg->to, User->Call) == 0) || ((User->flags & F_SYSOP) && (_stricmp(Msg->to, "SYSOP") == 0))) { if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D')) { if (Msg->status != 'Y') { Msg->status = 'Y'; Msg->datechanged=time(NULL); SaveMessageDatabase(); SendMessageReadEvent(Session->Callsign, Msg); } } } if (DisplayHTML && stristr(Message, "")) DisplayStyle = "div"; // Use div so HTML and XML are interpreted return sprintf(Reply, WebMailMsgTemplate, BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key, DownLoad, Key, Key, Key, DisplayStyle, Message, DisplayStyle); } // Remove B2 Headers (up to the File: Line) // ptr1 = strstr(MsgBytes, "Body:"); // if (ptr1) // MsgBytes = ptr1; } // Body may have cr cr lf which causes double space crcrptr = strstr(MsgBytes, "\r\r\n"); while (crcrptr) { *crcrptr = ' '; crcrptr = strstr(crcrptr, "\r\r\n"); } // Remove lf chars msgLen = RemoveLF(MsgBytes, msgLen); User->Total.MsgsSent[Index] ++; // User->Total.BytesForwardedOut[Index] += Length; // if body not UTF-8, convert it if (WebIsUTF8(MsgBytes, msgLen) == FALSE) { int code = TrytoGuessCode(MsgBytes, msgLen); UCHAR * UTF = malloc(msgLen * 3); if (code == 437) msgLen = Convert437toUTF8(MsgBytes, msgLen, UTF); else if (code == 1251) msgLen = Convert1251toUTF8(MsgBytes, msgLen, UTF); else msgLen = Convert1252toUTF8(MsgBytes, msgLen, UTF); free(MsgBytes); Save = MsgBytes = UTF; MsgBytes[msgLen] = 0; } // ptr += sprintf(ptr, "%s", MsgBytes); memcpy(ptr, MsgBytes, msgLen); ptr += msgLen; ptr[0] = 0; free(Save); ptr += sprintf(ptr, "\r\r[End of Message #%d from %s]\r", Number, Msg->from); if ((_stricmp(Msg->to, User->Call) == 0) || ((User->flags & F_SYSOP) && (_stricmp(Msg->to, "SYSOP") == 0))) { if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D')) { if (Msg->status != 'Y') { Msg->status = 'Y'; Msg->datechanged=time(NULL); SaveMessageDatabase(); SendMessageReadEvent(Session->Callsign, Msg); } } } } else { ptr += sprintf(ptr, "File for Message %d not found\r", Number); } if (DisplayHTML && stristr(Message, "")) DisplayStyle = "div"; // Use div so HTML and XML are interpreted return sprintf(Reply, WebMailMsgTemplate, BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key, DownLoad, Key, Key, Key, DisplayStyle, Message, DisplayStyle); } int KillWebMailMessage(char * Reply, char * Key, struct UserInfo * User, int Number) { struct MsgInfo * Msg; char Message[100] = ""; Msg = GetMsgFromNumber(Number); if (Msg == NULL) { sprintf(Message, "Message %d not found", Number); goto returnit; } if (OkToKillMessage(User->flags & F_SYSOP, User->Call, Msg)) { FlagAsKilled(Msg, TRUE); sprintf(Message, "Message #%d Killed\r", Number); goto returnit; } sprintf(Message, "Not your message\r"); returnit: return sprintf(Reply, WebMailMsgTemplate, BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key, "", Key, Key, Key, "div", Message, "div"); } void freeKeys(KeyValues * Keys) { while (Keys->Key) { free(Keys->Key); free(Keys->Value); Keys++; } } void FreeWebMailFields(WebMailInfo * WebMail) { // release any malloc'ed resources int i; char * SaveReply; int * SaveRlen; if (WebMail == NULL) return; if (WebMail->txtFile) free(WebMail->txtFile); if (WebMail->txtFileName) free(WebMail->txtFileName); if (WebMail->InputHTMLName) free(WebMail->InputHTMLName); if (WebMail->DisplayHTMLName) free(WebMail->DisplayHTMLName); if (WebMail->ReplyHTMLName) free(WebMail->ReplyHTMLName); if (WebMail->To) free(WebMail->To); if (WebMail->CC) free(WebMail->CC); if (WebMail->Subject) free(WebMail->Subject); if (WebMail->BID) free(WebMail->BID); if (WebMail->Body) free(WebMail->Body); if (WebMail->XML) free(WebMail->XML); if (WebMail->XMLName) free(WebMail->XMLName); if (WebMail->OrigTo) free(WebMail->OrigTo); if (WebMail->OrigSubject) free(WebMail->OrigSubject); if (WebMail->OrigBID) free(WebMail->OrigBID); if (WebMail->OrigBody) free(WebMail->OrigBody); freeKeys(WebMail->txtKeys); freeKeys(WebMail->XMLKeys); for (i = 0; i < WebMail->Files; i++) { free(WebMail->FileBody[i]); free(WebMail->FileName[i]); } if (WebMail->Header) free(WebMail->Header); if (WebMail->Footer) free(WebMail->Footer); SaveReply = WebMail->Reply; SaveRlen = WebMail->RLen; #ifndef WIN32 if (WebMail->iconv_toUTF8) iconv_close(WebMail->iconv_toUTF8); #endif memset(WebMail, 0, sizeof(WebMailInfo)); WebMail->Reply = SaveReply; WebMail->RLen = SaveRlen; return; } void ProcessWebMailMessage(struct HTTPConnectionInfo * Session, char * Key, BOOL LOCAL, char * Method, char * NodeURL, char * input, char * Reply, int * RLen, int InputLen) { char * URLParams = strlop(Key, '&'); int ReplyLen; char Appl = 'M'; // Webmail doesn't use the normal Mail Key. // webscript.js doesn't need a key if (_stricmp(NodeURL, "/WebMail/webscript.js") == 0) { if (jsTemplate) free(jsTemplate); jsTemplate = GetTemplateFromFile(2, "webscript.js"); ReplyLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" "Cache-Control: max-age=60\r\nContent-Type: text/javascript\r\n\r\n%s", (int)strlen(jsTemplate), jsTemplate); *RLen = ReplyLen; return; } // Neither do js or file downloads if (_memicmp(NodeURL, "/WebMail/WMFile/", 16) == 0) { int FileSize; char * MsgBytes; char MsgFile[512]; FILE * hFile; size_t ReadLen; char TimeString[64]; char FileTimeString[64]; struct stat STAT; char * FN = &NodeURL[16]; char * fileBit = FN; char * ext; UndoTransparency(FN); ext = strchr(FN, '.'); sprintf(MsgFile, "%s/%s", BPQDirectory, FN); while (strchr(fileBit, '/')) fileBit = strlop(fileBit, '/'); if (stat(MsgFile, &STAT) == -1) { *RLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n"); return; } hFile = fopen(MsgFile, "rb"); if (hFile == 0) { *RLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n"); return; } FileSize = STAT.st_size; MsgBytes = malloc(FileSize + 1); ReadLen = fread(MsgBytes, 1, FileSize, hFile); fclose(hFile); FormatTime2(FileTimeString, STAT.st_ctime); FormatTime2(TimeString, time(NULL)); if (_stricmp(ext, ".htm") == 0 || _stricmp(ext, ".html") == 0 || _stricmp(ext, ".css") == 0 || _stricmp(ext, ".js") == 0) { *RLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" "Content-Type: text/css\r\n" "Date: %s\r\n" "Last-Modified: %s\r\n" "\r\n%s", FileSize, TimeString, FileTimeString, MsgBytes); } else { *RLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n" "Content-Type: application/octet-stream\r\n" "Content-Disposition: attachment; filename=\"%s\"\r\n" "Date: %s\r\n" "Last-Modified: %s\r\n" "\r\n%s", FileSize, fileBit, TimeString, FileTimeString, MsgBytes); } free (MsgBytes); return; } Session = NULL; if (Key && Key[0]) Session = FindWMSession(Key); if (Session == NULL) { // Lost Session if (LOCAL) { Session = AllocateWebMailSession(); Key = Session->Key; if (SYSOPCall[0]) Session->User = LookupCall(SYSOPCall); else Session->User = LookupCall(BBSName); if (Session->User) { strcpy(NodeURL, "/WebMail/WebMail"); Session->WebMailSkip = 0; Session->WebMailLastUsed = time(NULL); } } else { // Send Login Page unless Signon request if (_stricmp(NodeURL, "/WebMail/Signon") != 0 || strcmp(Method, "POST") != 0) { ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName); *RLen = ReplyLen; return; } } } if (strcmp(Method, "POST") == 0) { if (_stricmp(NodeURL, "/WebMail/Signon") == 0) { char * msg = strstr(input, "\r\n\r\n"); // End of headers char * user, * password, * Key; char Msg[128]; int n; if (msg) { struct UserInfo * User; if (strstr(msg, "Cancel=Cancel")) { *RLen = sprintf(Reply, ""); return; } // Webmail Gets Here with a dummy Session Session = AllocateWebMailSession(); Session->WebMail->Reply = Reply; Session->WebMail->RLen = RLen; Key = Session->Key; user = strtok_s(&msg[9], "&", &Key); password = strtok_s(NULL, "=", &Key); password = Key; Session->User = User = LookupCall(user); if (User) { // Check Password if (password[0] && strcmp(User->pass, password) == 0) { // send Message Index Session->WebMailLastUsed = time(NULL); Session->WebMailSkip = 0; Session->WebMailMyTX = FALSE; Session->WebMailMyRX = FALSE; Session->WebMailMine = FALSE; if (WebMailTemplate) { free(WebMailTemplate); WebMailTemplate = NULL; } if (User->flags & F_Excluded) { n = sprintf_s(Msg, sizeof(Msg), "Webmail Connect from %s Rejected by Exclude Flag", _strupr(user)); WriteLogLine(NULL, '|',Msg, n, LOG_BBS); ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName); *RLen = ReplyLen; return; } *RLen = SendWebMailHeader(Reply, Session->Key, Session); n=sprintf_s(Msg, sizeof(Msg), "Webmail Connect from %s", _strupr(user)); WriteLogLine(NULL, '|',Msg, n, LOG_BBS); return; } } // Bad User or Pass ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName); *RLen = ReplyLen; return; } } Session->WebMail->Reply = Reply; Session->WebMail->RLen = RLen; if (_stricmp(NodeURL, "/WebMail/EMSave") == 0) { // Save New Message SaveNewMessage(Session, input, Reply, RLen, Key, InputLen); return; } if (_stricmp(NodeURL, "/WebMail/Submit") == 0) { // Get the POST data from the page and place in message char * param = strstr(input, "\r\n\r\n"); // End of headers WebMailInfo * WebMail = Session->WebMail; if (WebMail == NULL) return; // Can't proceed if we have no info on form ProcessFormInput(Session, input, Reply, RLen, InputLen); return; } if (_stricmp(NodeURL, "/WebMail/FormMsgSave") == 0) { // Save New Message SaveTemplateMessage(Session, input, Reply, RLen, Key); return; } if (_stricmp(NodeURL, "/WebMail/GetTemplates") == 0) { SendTemplateSelectScreen(Session, input, InputLen); return; } // End of POST section } if (_stricmp(NodeURL, "/WebMail/WMLogout") == 0) { Session->Key[0] = 0; Session->WebMailLastUsed = 0; ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName); *RLen = ReplyLen; return; } if ((_stricmp(NodeURL, "/WebMail/MailEntry") == 0) || (_stricmp(NodeURL, "/WebMail") == 0) || (_stricmp(NodeURL, "/WebMail/") == 0)) { // Entry from Menu if signed in, continue. If not and Localhost // signin as sysop. if (Session->User == NULL) { // Not yet signed in if (LOCAL) { // Webmail Gets Here with a dummy Session Session = AllocateWebMailSession(); Session->WebMail->Reply = Reply; Session->WebMail->RLen = RLen; Key = Session->Key; if (SYSOPCall[0]) Session->User = LookupCall(SYSOPCall); else Session->User = LookupCall(BBSName); if (Session->User) { strcpy(NodeURL, "/WebMail/WebMail"); Session->WebMailSkip = 0; Session->WebMailLastUsed = time(NULL); } } else { // Send Login Page ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName); *RLen = ReplyLen; return; } } } Session->WebMail->Reply = Reply; Session->WebMail->RLen = RLen; if (_stricmp(NodeURL, "/WebMail/WebMail") == 0) { if (WebMailTemplate) { free(WebMailTemplate); WebMailTemplate = NULL; } Session->WebMailSkip = 0; Session->WebMailMine = FALSE; Session->WebMailMyTX = FALSE; Session->WebMailMyRX = FALSE; *RLen = SendWebMailHeader(Reply, Session->Key, Session); return; } if (_stricmp(NodeURL, "/WebMail/WMAll") == 0) { Session->WebMailSkip = 0; Session->WebMailTypes[0] = 0; Session->WebMailMine = FALSE; Session->WebMailMyTX = FALSE; Session->WebMailMyRX = FALSE; *RLen = SendWebMailHeader(Reply, Session->Key, Session); return; } if (_stricmp(NodeURL, "/WebMail/WMB") == 0) { Session->WebMailSkip = 0; strcpy(Session->WebMailTypes, "B"); Session->WebMailMine = FALSE; Session->WebMailMyTX = FALSE; Session->WebMailMyRX = FALSE; *RLen = SendWebMailHeader(Reply, Session->Key, Session); return; } if (_stricmp(NodeURL, "/WebMail/WMP") == 0) { Session->WebMailSkip = 0; strcpy(Session->WebMailTypes, "P"); Session->WebMailMine = FALSE; Session->WebMailMyTX = FALSE; Session->WebMailMyRX = FALSE; *RLen = SendWebMailHeader(Reply, Session->Key, Session); return; } if (_stricmp(NodeURL, "/WebMail/WMT") == 0) { Session->WebMailSkip = 0; strcpy(Session->WebMailTypes, "T"); Session->WebMailMine = FALSE; Session->WebMailMyTX = FALSE; Session->WebMailMyRX = FALSE; *RLen = SendWebMailHeader(Reply, Session->Key, Session); return; } if (_stricmp(NodeURL, "/WebMail/WMMine") == 0) { Session->WebMailSkip = 0; Session->WebMailTypes[0] = 0; Session->WebMailMine = TRUE; Session->WebMailMyTX = FALSE; Session->WebMailMyRX = FALSE; *RLen = SendWebMailHeader(Reply, Session->Key, Session); return; } if (_stricmp(NodeURL, "/WebMail/WMtoMe") == 0) { Session->WebMailSkip = 0; Session->WebMailTypes[0] = 0; Session->WebMailMine = FALSE; Session->WebMailMyTX = FALSE; Session->WebMailMyRX = TRUE; *RLen = SendWebMailHeader(Reply, Session->Key, Session); return; } if (_stricmp(NodeURL, "/WebMail/WMfromMe") == 0) { Session->WebMailSkip = 0; Session->WebMailTypes[0] = 0; Session->WebMailMine = TRUE; Session->WebMailMyTX = TRUE; Session->WebMailMyRX = FALSE; *RLen = SendWebMailHeader(Reply, Session->Key, Session); return; } if (_stricmp(NodeURL, "/WebMail/WMSame") == 0) { *RLen = SendWebMailHeader(Reply, Session->Key, Session); return; } if (_stricmp(NodeURL, "/WebMail/WMAuto") == 0) { // Auto Refresh Version of index page. Uses Web Sockets char Page[4096]; char WebSockPage[] = "\r\n" " \r\n" " \r\n" " \r\n" " \r\n" "\r\n" "\r\n" "\r\n" "WebMail \r\n" "\r\n" "\r\n" "

%s Webmail Interface - User %s - Message List

\r\n" "\r\n" "\r\n" "\r\n" "\r\n" "\r\n" "\r\n" "\r\n" "\r\n" "\r\n" "\r\n" "\r\n" "\r\n" "
BullsPersonalNTSAll TypesMineMy SentMy RxedAuto RefreshSend MessageLogoutNode Menu
\r\n" "
\r\n" "
Waiting for data...
\r\n" "\r\n"; sprintf(Page, WebSockPage, Key, Key ,BBSName, Session->User->Call, Key, Key, Key, Key, Key, Key, Key, Key, Key, Key); *RLen = sprintf(Reply, "%s", Page); return; } if (memcmp(NodeURL, "/WebMail/QuoteOriginal/", 15) == 0) { // Reply to Message int n, len; struct MsgInfo * Msg; char Message[100] = ""; char Title[100]; char * MsgBytes, * Save, * NewBytes; char * ptr; char * ptr1, * ptr2; char * EncodedTitle; n = Session->WebMail->CurrentMessageIndex; Msg = GetMsgFromNumber(n); if (Msg == NULL) { sprintf(Message, "Message %d not found", n); *RLen = sprintf(Reply, "%s", Message); return; } Session->WebMail->Msg = Msg; if (stristr(Msg->title, "Re:") == 0) sprintf(Title, "Re:%s", Msg->title); else sprintf(Title, "%s", Msg->title); MsgBytes = Save = ReadMessageFile(n); ptr = NewBytes = malloc((Msg->length * 2) + 256); // Copy a line at a time with "> " in front of each ptr += sprintf(ptr, "%s", "\r\n\r\n\r\n\r\n\r\nOriginal Message\r\n\r\n> "); ptr1 = ptr2 = MsgBytes; len = (int)strlen(MsgBytes); while (len-- > 0) { *ptr++ = *ptr1; if (*(ptr1) == '\n') { *ptr++ = '>'; *ptr++ = ' '; } ptr1++; } *ptr++ = 0; EncodedTitle = doXMLTransparency(Msg->title); *RLen = sprintf(Reply, MsgInputPage, Key, Msg->from, "", EncodedTitle , NewBytes); free(EncodedTitle); free(MsgBytes); free(NewBytes); return; } if (memcmp(NodeURL, "/WebMail/Reply/", 15) == 0) { // Reply to Message int n = atoi(&NodeURL[15]); struct MsgInfo * Msg; char Message[100] = ""; char Title[100]; char * EncodedTitle; // Quote Original char Button[] = "      " ""; char Temp[1024]; char ReplyAddr[128]; Msg = GetMsgFromNumber(n); if (Msg == NULL) { sprintf(Message, "Message %d not found", n); *RLen = sprintf(Reply, "%s", Message); return; } Session->WebMail->Msg = Msg; // See if the message was displayed in an HTML form with a reply template *RLen = ReplyToFormsMessage(Session, Msg, Reply, FALSE); // If couldn't build reply form use normal text reply if (*RLen) return; sprintf(Temp, Button, Key); if (stristr(Msg->title, "Re:") == 0) sprintf(Title, "Re:%s", Msg->title); else sprintf(Title, "%s", Msg->title); strcpy(ReplyAddr, Msg->from); strcat(ReplyAddr, Msg->emailfrom); EncodedTitle = doXMLTransparency(Msg->title); *RLen = sprintf(Reply, MsgInputPage, Key, Msg->from, Temp, EncodedTitle , ""); free(EncodedTitle); return; } if (strcmp(NodeURL, "/WebMail/WM") == 0) { // Read Message int n = 0; if (URLParams) n = atoi(URLParams); if (WebMailMsgTemplate) free(WebMailMsgTemplate); WebMailMsgTemplate = GetTemplateFromFile(5, "WebMailMsg.txt"); *RLen = ViewWebMailMessage(Session, Reply, n, TRUE); return; } if (strcmp(NodeURL, "/WebMail/WMPrev") == 0) { // Read Previous Message int m; struct MsgInfo * Msg; struct UserInfo * User = Session->User; for (m = Session->WebMail->CurrentMessageIndex - 1; m >= 1; m--) { Msg = GetMsgFromNumber(m); if (Msg == 0 || Msg->type == 0 || Msg->status == 0) continue; // Protect against corrupt messages if (Msg && CheckUserMsg(Msg, User->Call, User->flags & F_SYSOP)) { // Display if it is the right type and in the page range we want if (Session->WebMailTypes[0] && strchr(Session->WebMailTypes, Msg->type) == 0) continue; // All Types or right Type. Check Mine Flag if (Session->WebMailMine) { // Only list if to or from me if (strcmp(User->Call, Msg->to) != 0 && strcmp(User->Call, Msg->from) != 0) continue; } if (Session->WebMailMyTX) { // Only list if to or from me if (strcmp(User->Call, Msg->from) != 0) continue; } if (Session->WebMailMyRX) { // Only list if to or from me if (strcmp(User->Call, Msg->to) != 0) continue; } *RLen = ViewWebMailMessage(Session, Reply, m, TRUE); return; } } // No More *RLen = sprintf(Reply, "", Session->Key); return; } if (strcmp(NodeURL, "/WebMail/WMNext") == 0) { // Read Previous Message int m; struct MsgInfo * Msg; struct UserInfo * User = Session->User; for (m = Session->WebMail->CurrentMessageIndex + 1; m <= LatestMsg; m++) { Msg = GetMsgFromNumber(m); if (Msg == 0 || Msg->type == 0 || Msg->status == 0) continue; // Protect against corrupt messages if (Msg && CheckUserMsg(Msg, User->Call, User->flags & F_SYSOP)) { // Display if it is the right type and in the page range we want if (Session->WebMailTypes[0] && strchr(Session->WebMailTypes, Msg->type) == 0) continue; // All Types or right Type. Check Mine Flag if (Session->WebMailMine) { // Only list if to or from me if (strcmp(User->Call, Msg->to) != 0 && strcmp(User->Call, Msg->from) != 0) continue; } if (Session->WebMailMyTX) { // Only list if to or from me if (strcmp(User->Call, Msg->from) != 0) continue; } if (Session->WebMailMyRX) { // Only list if to or from me if (strcmp(User->Call, Msg->to) != 0) continue; } *RLen = ViewWebMailMessage(Session, Reply, m, TRUE); return; } } // No More *RLen = sprintf(Reply, "", Session->Key); return; } if (strcmp(NodeURL, "/WebMail/DisplayText") == 0) { // Read Message int n = 0; if (URLParams) n = atoi(URLParams); if (WebMailMsgTemplate) free(WebMailMsgTemplate); WebMailMsgTemplate = GetTemplateFromFile(5, "WebMailMsg.txt"); *RLen = ViewWebMailMessage(Session, Reply, n, FALSE); return; } if (memcmp(NodeURL, "/WebMail/WMDel/", 15) == 0) { // Kill Message int n = atoi(&NodeURL[15]); *RLen = KillWebMailMessage(Reply, Session->Key, Session->User, n); return; } if (_stricmp(NodeURL, "/WebMail/NewMsg") == 0) { // Add HTML Template Button if we have any HTML Form char Button[] = "      " ""; char Temp[1024]; FreeWebMailFields(Session->WebMail); // Tidy up for new message sprintf(Temp, Button, Key); if (FormDirCount == 0) *RLen = sprintf(Reply, MsgInputPage, Key, "", "", "", ""); else *RLen = sprintf(Reply, MsgInputPage, Key, "", Temp, "", ""); return; } if (_memicmp(NodeURL, "/WebMail/GetPage/", 17) == 0) { // Read and Parse Template File GetPage(Session, NodeURL); return; } if (_memicmp(NodeURL, "/WebMail/GetList/", 17) == 0) { // Send Select Template Popup char * SubDir; int DirNo = 0; int SubDirNo = 0; char popup[10000]; char popuphddr[] = "" "" "

" "Select Required Template from %s

" "

", popup); *RLen = sprintf(Reply, "%s", popup); return; } if (_stricmp(NodeURL, "/WebMail/DL") == 0) { getAttachmentList(Session, Reply, RLen, URLParams); return; } if (_stricmp(NodeURL, "/WebMail/GetDownLoad") == 0) { DownloadAttachments(Session, Reply, RLen, URLParams); return; } if (_stricmp(NodeURL, "/WebMail/DoSelect") == 0) { // User has selected item from Template "; char NewGroup [] = "" "

", popup); *WebMail->RLen = sprintf(WebMail->Reply, "%s", popup); free(Boundary); return; } static char WinlinkAddr[] = "WINLINK.ORG"; VOID SaveNewMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest, int InputLen) { int i, ReplyLen = 0; struct MsgInfo * Msg; FILE * hFile; int Template=0; char * via = NULL; BIDRec * BIDRec; char MsgFile[MAX_PATH]; size_t WriteLen=0; char * HDest; char * HDestCopy; char * HDestRest; char * Vptr = NULL; char * FileList = NULL; char Prompt[256] = "Message Saved"; char OrigTo[256]; WebMailInfo * WebMail = Session->WebMail; struct UserInfo * user; CIRCUIT conn; // So we can have attachments input is now Content-Type: multipart/form-data; char * Input; size_t MsgLen = 0; char * Boundary; strcpy(conn.Callsign, Session->User->Call); Input = MsgPtr; Boundary = initMultipartUnpack(&Input); if (Boundary == NULL) return; // Can't work without one // Input points to start of part. Normally preceeded by \r\n which is Boundary Terminator. If preceeded by -- we have used last part while(Input && Input[-1] != '-') { char * Name, * Value; int ValLen; if (unpackPart(Boundary, &Input, &Name, &Value, &ValLen, MsgPtr + InputLen) == FALSE) { // ReportCorrupt(WebMail); free(Boundary); return; } if (SaveInputValue(WebMail, Name, Value, ValLen) == FALSE) { *RLen = sprintf(Reply, "", Session->Key); return; } } if (WebMail->txtFileName) { // Processing Form Input SaveTemplateMessage(Session, MsgPtr, Reply, RLen, Rest); // Prevent re-entry free(WebMail->txtFileName); WebMail->txtFileName = NULL; return; } // If we aren't using a template then all the information is in the WebMail fields, as we haven't been here before. strlop(WebMail->BID, ' '); if (strlen(WebMail->BID) > 12) WebMail->BID[12] = 0; UndoTransparency(WebMail->BID); UndoTransparency(WebMail->To); UndoTransparency(WebMail->Subject); UndoTransparency(WebMail->Body); MsgLen = strlen(WebMail->Body); // We will need to mess about with To field. Create a copy so the original can go in B2 header if we use one if (WebMail->To[0] == 0) { *RLen = sprintf(Reply, "%s", ""); FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise return; } if (strlen(WebMail->To) > 255) { *RLen = sprintf(Reply, "%s", ""); FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise return; } HDest = _strdup(WebMail->To); if (strlen(WebMail->BID)) { if (LookupBID(WebMail->BID)) { // Duplicate bid *RLen = sprintf(Reply, "%s", ""); FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise return; } } if (WebMail->Type == 'B') { if (RefuseBulls) { *RLen = sprintf(Reply, "%s", ""); FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise return; } } // ?? Can we just loop though the rest of the code to allow multiple dests ?? HDestCopy = HDest; while (HDest && HDest[0]) { HDestRest = strlop(HDest, ';'); Msg = AllocateMsgRecord(); // Set number here so they remain in sequence Msg->number = ++LatestMsg; MsgnotoMsg[Msg->number] = Msg; strcpy(Msg->from, Session->User->Call); if (_memicmp(HDest, "rms:", 4) == 0 || _memicmp(HDest, "rms/", 4) == 0) { Vptr=&HDest[4]; strcpy(Msg->to, "RMS"); } else if (_memicmp(HDest, "smtp:", 5) == 0) { if (ISP_Gateway_Enabled) { Vptr=&HDest[5]; Msg->to[0] = 0; } } else if (strchr(HDest, '@')) { strcpy(OrigTo, HDest); Vptr = strlop(HDest, '@'); if (Vptr) { // If looks like a valid email address, treat as such if (strlen(HDest) > 6 || !CheckifPacket(Vptr)) { // Assume Email address Vptr = OrigTo; if (FindRMS() || strchr(Vptr, '!')) // have RMS or source route strcpy(Msg->to, "RMS"); else if (ISP_Gateway_Enabled) Msg->to[0] = 0; else if (isAMPRMsg(OrigTo)) strcpy(Msg->to, "RMS"); // Routing will redirect it else { *RLen = sprintf(Reply, "%s", ""); FreeWebMailFields(WebMail); // We will reprocess message and attachments so reinitialise return; } } else strcpy(Msg->to, _strupr(HDest)); } } else { // No @ if (strlen(HDest) > 6) HDest[6] = 0; strcpy(Msg->to, _strupr(HDest)); } if (SendBBStoSYSOPCall) if (_stricmp(HDest, BBSName) == 0) strcpy(Msg->to, SYSOPCall); if (Vptr) { if (strlen(Vptr) > 40) Vptr[40] = 0; strcpy(Msg->via, _strupr(Vptr)); } else { // No via. If not local user try to add BBS struct UserInfo * ToUser = LookupCall(Msg->to); if (ToUser) { // Local User. If Home BBS is specified, use it if (ToUser->flags & F_RMSREDIRECT) { // sent to Winlink strcpy(Msg->via, WinlinkAddr); sprintf(Prompt, "Redirecting to winlink.org\r"); } else if (ToUser->HomeBBS[0]) { strcpy(Msg->via, ToUser->HomeBBS); sprintf(Prompt, "%s added from HomeBBS. Message Saved", Msg->via); } } else { // Not local user - Check WP WPRecP WP = LookupWP(Msg->to); if (WP) { strcpy(Msg->via, WP->first_homebbs); sprintf(Prompt, "%s added from WP", Msg->via); } } } if (strlen(WebMail->Subject) > 60) WebMail->Subject[60] = 0; strcpy(Msg->title, WebMail->Subject); Msg->type = WebMail->Type; if (Session->User->flags & F_HOLDMAIL) { int Length=0; char * MailBuffer = malloc(100); char Title[100]; Msg->status = 'H'; Length = sprintf(MailBuffer, "Message %d Held\r\n", Msg->number); sprintf(Title, "Message %d Held - %s", Msg->number, "User has Hold Messages flag set"); SendMessageToSYSOP(Title, MailBuffer, Length); } else Msg->status = 'N'; if (strlen(WebMail->BID) == 0) sprintf(Msg->bid, "%d_%s", LatestMsg, BBSName); else strcpy(Msg->bid, WebMail->BID); Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL); BIDRec = AllocateBIDRecord(); strcpy(BIDRec->BID, Msg->bid); BIDRec->mode = Msg->type; BIDRec->u.msgno = LOWORD(Msg->number); BIDRec->u.timestamp = LOWORD(time(NULL)/86400); Msg->length = (int)MsgLen + WebMail->HeaderLen + WebMail->FooterLen; sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); // BuildFormMessage(Session, Msg); if (WebMail->Files) { // Send as B2 char * B2Header = BuildB2Header(WebMail, Msg, NULL, 0); hFile = fopen(MsgFile, "wb"); if (hFile) { WriteLen = fwrite(B2Header, 1, strlen(B2Header), hFile); WriteLen += fwrite(WebMail->Header, 1, WebMail->HeaderLen, hFile); WriteLen += fwrite(WebMail->Body, 1, MsgLen, hFile); WriteLen += fwrite(WebMail->Footer, 1, WebMail->FooterLen, hFile); WriteLen += fwrite("\r\n", 1, 2, hFile); for (i = 0; i < WebMail->Files; i++) { WriteLen += fwrite(WebMail->FileBody[i], 1, WebMail->FileLen[i], hFile); WriteLen += fwrite("\r\n", 1, 2, hFile); } fclose(hFile); free(B2Header); Msg->length = (int)WriteLen; } } else { hFile = fopen(MsgFile, "wb"); if (hFile) { WriteLen += fwrite(WebMail->Header, 1, WebMail->HeaderLen, hFile); WriteLen += fwrite(WebMail->Body, 1, MsgLen, hFile); WriteLen += fwrite(WebMail->Footer, 1, WebMail->FooterLen, hFile); fclose(hFile); } } MatchMessagetoBBSList(Msg, &conn); BuildNNTPList(Msg); // Build NNTP Groups list if (Msg->status != 'H' && Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0) Msg->status = '$'; // Has forwarding if (EnableUI) SendMsgUI(Msg); user = LookupCall(Msg->to); // If Event Notifications enabled report a new message event SendNewMessageEvent(user->Call, Msg); #ifndef NOMQTT if (MQTT) MQTTMessageEvent(Msg); #endif if (user && (user->flags & F_APRSMFOR)) { char APRS[128]; char Call[16]; int SSID = user->flags >> 28; if (SSID) sprintf(Call, "%s-%d", Msg->to, SSID); else strcpy(Call, Msg->to); sprintf(APRS, "New BBS Message %s From %s", Msg->title, Msg->from); APISendAPRSMessage(APRS, Call); } HDest = HDestRest; } *RLen = SendWebMailHeaderEx(Reply, Session->Key, Session, Prompt); SaveMessageDatabase(); SaveBIDDatabase(); FreeWebMailFields(WebMail); free(HDestCopy); return; } // RMS Express Forms Support char * GetHTMLViewerTemplate(char * FN) { int i, j, k, l; // Seach list of forms for base file (without .html) for (i = 0; i < FormDirCount; i++) { struct HtmlFormDir * Dir = HtmlFormDirs[i]; for (j = 0; j < Dir->FormCount; j++) { if (strcmp(FN, Dir->Forms[j]->FileName) == 0) { return CheckFile(Dir, FN); } } if (Dir->DirCount) { for (l = 0; l < Dir->DirCount; l++) { struct HtmlFormDir * SDir = Dir->Dirs[l]; if (SDir->DirCount) { struct HtmlFormDir * SSDir = SDir->Dirs[0]; int x = 1; } for (k = 0; k < SDir->FormCount; k++) { if (_stricmp(FN, SDir->Forms[k]->FileName) == 0) { return CheckFile(SDir, SDir->Forms[k]->FileName); } } if (SDir->DirCount) { struct HtmlFormDir * SSDir = SDir->Dirs[0]; int x = 1; } } } } return NULL; } VOID GetReply(struct HTTPConnectionInfo * Session, char * NodeURL) { } VOID GetPage(struct HTTPConnectionInfo * Session, char * NodeURL) { // Read the HTML Template file and do any needed substitutions WebMailInfo * WebMail = Session->WebMail; KeyValues * txtKey = WebMail->txtKeys; int DirNo; char * ptr; int FileNo = 0; char * SubDir; int SubDirNo; int i; struct HtmlFormDir * Dir; char * Template; char * inptr; char FormDir[MAX_PATH] = ""; char FN[MAX_PATH] = ""; char * InputName = NULL; // HTML to input message char * ReplyName = NULL; char * To = NULL; char * CC = NULL; char * BID = NULL; char Type = 0; char * Subject = NULL; char * MsgBody = NULL; char * varptr; char * endptr; size_t varlen, vallen = 0; char val[256]=""; // replacement text char var[100] = "\""; char * MsgBytes; char Submit[64]; if (NodeURL == NULL) { //rentry after processing or substitutions // if Dir not specified search all for Filename if (Dir == NULL) { for (i = 0; i < FormDirCount; i++) { int n; Dir = HtmlFormDirs[i]; MsgBytes = CheckFile(Dir, WebMail->txtFileName); if (MsgBytes) goto gotFile; // Recurse any Subdirs n = 0; while (n < Dir->DirCount) { MsgBytes = CheckFile(Dir->Dirs[n], FN); if (MsgBytes) { Dir = Dir->Dirs[n]; goto gotFile; } n++; } } return; } else MsgBytes = CheckFile(Dir, WebMail->txtFileName); gotFile: WebMail->Dir = Dir; if (WebMail->txtFile) free(WebMail->txtFile); WebMail->txtFile = MsgBytes; reEnter: if (ParsetxtTemplate(Session, Dir, WebMail->txtFileName, FALSE) == FALSE) { // Template has or if (WebMail->InputHTMLName == NULL) { // This is a plain text template without HTML /* if (To == NULL) To = ""; if (To[0] == 0 && WebMail->To && WebMail->To[0]) To = WebMail->To; if (CC == NULL) CC = ""; if (CC[0] == 0 && WebMail->CC && WebMail->CC[0]) CC = WebMail->CC; if (Subject == NULL) Subject = ""; if (Subject[0] == 0 && WebMail->Subject && WebMail->Subject[0]) Subject = WebMail->Subject; if (MsgBody == NULL) MsgBody = ""; if (MsgBody[0] == 0 && WebMail->Body && WebMail->Body[0]) MsgBody = WebMail->Body; *WebMail->RLen = sprintf(WebMail->Reply, CheckFormMsgPage, Session->Key, To, CC, Subject, MsgBody); */ return *WebMail->RLen; } Template = CheckFile(WebMail->Dir, WebMail->InputHTMLName); if (Template == NULL) { // Missing HTML *WebMail->RLen = sprintf(WebMail->Reply, "", WebMail->InputHTMLName); return *WebMail->RLen; } // I've going to update the template in situ, as I can't see a better way // of making sure all occurances of variables in any order are substituted. // The space allocated to Template is twice the size of the file // to allow for insertions UpdateFormAction(Template, Session->Key); // Update "Submit" Action // Search for "{var }" strings in form and replace with // corresponding variable from XML while (txtKey->Key) { char Key[256] = "{var "; strcpy(&Key[5], txtKey->Key); strcat(Key, "}"); inptr = Template; varptr = stristr(inptr, Key); while (varptr) { // Move the remaining message up/down the buffer to make space for substitution varlen = (int)strlen(Key); if (txtKey->Value) vallen = (int)strlen(txtKey->Value); else vallen = 0; endptr = varptr + varlen; memmove(varptr + vallen, endptr, strlen(endptr) + 1); // copy null on end memcpy(varptr, txtKey->Value, vallen); inptr = endptr + 1; varptr = stristr(inptr, Key); } txtKey++; } // Remove from end as we add it on later ptr = stristr(Template, ""); if (ptr) *ptr = 0; Len = sprintf(Reply, "%s", Template); free(Template); return Len; } char * CheckFile(struct HtmlFormDir * Dir, char * FN) { struct stat STAT; FILE * hFile; char MsgFile[MAX_PATH]; char * MsgBytes; int ReadLen; int FileSize; #ifndef WIN32 // Need to do case insensitive file search DIR *dir; struct dirent *entry; char name[256]; sprintf(name, "%s/%s/%s", BPQDirectory, Dir->FormSet, Dir->DirName); if (!(dir = opendir(name))) { Debugprintf("cant open forms dir %s %s %d", Dir->DirName, name, errno); return 0; } while ((entry = readdir(dir)) != NULL) { if (entry->d_type == DT_DIR) continue; if (stricmp(entry->d_name, FN) == 0) { sprintf(MsgFile, "%s/%s/%s/%s", GetBPQDirectory(), Dir->FormSet, Dir->DirName, entry->d_name); break; } } closedir(dir); #else sprintf(MsgFile, "%s/%s/%s/%s", GetBPQDirectory(), Dir->FormSet, Dir->DirName, FN); #endif printf("%s\n", MsgFile); if (stat(MsgFile, &STAT) != -1) { hFile = fopen(MsgFile, "rb"); if (hFile == 0) { MsgBytes = _strdup("File is missing"); return MsgBytes; } FileSize = STAT.st_size; MsgBytes = malloc(FileSize * 10); // Allow plenty of room for template substitution ReadLen = (int)fread(MsgBytes, 1, FileSize, hFile); MsgBytes[FileSize] = 0; fclose(hFile); printf("%d %s\n", strlen(MsgBytes), MsgBytes); return MsgBytes; } return NULL; } BOOL DoSelectPrompt(struct HTTPConnectionInfo * Session, char * Select) { // Send a Popup window to select value. Reply handling code will update template then reenter ParsetxtTemplate char popuphddr[] = "" "" "
%s

" "" "
'); if (ptr) *ptr = 0; ptr = SelCopy; if (*ptr == '"') { // String has " " round it ptr++; ptr1 = strchr(ptr, '"'); if (ptr1 == NULL) goto returnDuff; *(ptr1++) = 0; prompt = ptr; } else { // Normal comma terminated ptr1 = strchr(ptr, ','); if (ptr1 == NULL) goto returnDuff; *(ptr1++) = 0; prompt = ptr; } ptr = ptr1; while (ptr && ptr[0]) { if (*ptr == '"') { // String has " " round it ptr++; ptr1 = strchr(ptr, '"'); if (ptr1 == NULL) goto returnDuff; *(ptr1++) = 0; while(ptr1 && *ptr1 == ',') ptr1++; } else { // Normal comma terminated ptr1 = strchr(ptr, ','); if (ptr1) *(ptr1++) = 0; } var[vars++] = ptr; ptr = ptr1; } sprintf(popup, popuphddr, Session->Key, prompt, vars + 1); for (i = 0; i < vars; i++) { char * key = strlop(var[i], '='); if (key == NULL) key = var[i]; sprintf(popup, "%s

", popup); *WebMail->RLen = sprintf(WebMail->Reply, "%s", popup); free(SelCopy); return TRUE; returnDuff: *WebMail->RLen = sprintf(WebMail->Reply, "", Session->Key); free(SelCopy); return TRUE; } BOOL DoAskPrompt(struct HTTPConnectionInfo * Session, char * Select) { return TRUE; } VOID ProcessSelectResponse(struct HTTPConnectionInfo * Session, char * URLParams) { // User has entered a response for a Template RLen = sprintf(WebMail->Reply, "", Session->Key); return; } varptr = Select; endptr = strchr(Select, '>'); if (endptr == NULL) { *WebMail->RLen = sprintf(WebMail->Reply, "", Session->Key); return; } *endptr = 0; varlen = endptr - varptr; valptr = URLParams; // Move the remaining message up/down the buffer to make space for substitution vallen = strlen(valptr); endptr = varptr + varlen; memcpy(varptr, valptr, vallen); memmove(varptr + vallen, endptr + 1, strlen(endptr + 1) + 1); // copy null on end if (WebMail->isReply) *WebMail->RLen = ReplyToFormsMessage(Session, Session->Msg, WebMail->Reply, TRUE); else GetPage(Session, NULL); return ; } BOOL ParsetxtTemplate(struct HTTPConnectionInfo * Session, struct HtmlFormDir * Dir, char * FN, BOOL isReply) { WebMailInfo * WebMail = Session->WebMail; KeyValues * txtKey = WebMail->txtKeys; char * MsgBytes; char * txtFile; char * ptr, *ptr1; char * InputName = NULL; // HTML to input message char * ReplyName = NULL; char * To = NULL; char * Subject = NULL; char * MsgBody = NULL; char * Select = NULL; char * Ask = NULL; char Date[16]; char UDate[16]; char DateTime[32]; char UDateTime[32]; char Day[16]; char UDay[16]; char UDTG[32]; char Seq[16]; char FormDir[MAX_PATH]; double Lat; double Lon; char LatString[32], LonString[32], GPSString[32]; BOOL GPSOK; struct tm * tm; time_t NOW; // Template is now read before entering here MsgBytes = WebMail->txtFile; // if Template uses tm_year + 1900,tm->tm_mon + 1, tm->tm_mday); sprintf(DateTime, "%04d-%02d-%02d %02d:%02d:%02d", tm->tm_year + 1900,tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); strcpy(Day, longday[tm->tm_wday]); tm = gmtime(&NOW); sprintf(UDate, "%04d-%02d-%02dZ", tm->tm_year + 1900,tm->tm_mon + 1, tm->tm_mday); sprintf(UDateTime, "%04d-%02d-%02d %02d:%02d:%02dZ", tm->tm_year + 100,tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); sprintf(UDTG, "%02d%02d%02dZ %s %04d", tm->tm_mday, tm->tm_hour, tm->tm_min, month[tm->tm_mon], tm->tm_year + 1900); strcpy(UDay, longday[tm->tm_wday]); sprintf(Seq, "%d", Session->User->WebSeqNo); sprintf(FormDir, "/WebMail/WMFile/%s/%s/", WebMail->Dir->FormSet, WebMail->Dir->DirName); // Keep SeqNo at front txtKey->Key = _strdup(""); txtKey++->Value = _strdup(Seq); txtKey->Key = _strdup(""); txtKey++->Value = _strdup(DateTime); txtKey->Key = _strdup(""); txtKey++->Value = _strdup(UDateTime); txtKey->Key = _strdup(""); txtKey++->Value = _strdup(Date); txtKey->Key = _strdup(""); txtKey++->Value = _strdup(UDate); txtKey->Key = _strdup("