/* 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 Fvoideven 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 */ // Mail and Chat Server for BPQ32 Packet Switch // // White Pages Database Support Routines #include "bpqmail.h" int CurrentWPIndex; char CurrentWPCall[10]; time_t LASTWPSendTime; VOID DoWPUpdate(WPRec *WP, char Type, char * Name, char * HA, char * QTH, char * ZIP, time_t WPDate); VOID Do_Save_WPRec(HWND hDlg); VOID SaveInt64Value(config_setting_t * group, char * name, long long value); VOID SaveIntValue(config_setting_t * group, char * name, int value); VOID SaveStringValue(config_setting_t * group, char * name, char * value); void MQTTMessageEvent(void* message); WPRec * AllocateWPRecord() { WPRec * WP = zalloc(sizeof (WPRec)); GetSemaphore(&AllocSemaphore, 0); WPRecPtr=realloc(WPRecPtr,(++NumberofWPrecs+1) * sizeof(void *)); WPRecPtr[NumberofWPrecs]= WP; FreeSemaphore(&AllocSemaphore); return WP; } int BadCall(char * Call) { if (_stricmp(Call, "RMS") == 0) return 1; if (_stricmp(Call, "SYSTEM") == 0) return 1; if (_stricmp(Call, "SWITCH") == 0) return 1; if (_stricmp(Call, "SYSOP") == 0) return 1; if (_memicmp(Call, "SMTP", 4) == 0) return 1; if (_memicmp(Call, "SMTP:", 5) == 0) return 1; if (_stricmp(Call, "AMPR") == 0) return 1; if (_stricmp(Call, "FILE") == 0) return 1; if (_memicmp(Call, "MCAST", 5) == 0) return 1; if (_memicmp(Call, "SYNC", 5) == 0) return 1; return 0; } extern config_t cfg; VOID GetWPDatabase() { WPRec WPRec; FILE * Handle; int ReadLen; WPRecP WP; char CfgName[MAX_PATH]; long long val; config_t wpcfg; config_setting_t * group, * wpgroup; int i = 1; struct stat STAT; // If WP info is in main config file, use it group = config_lookup (&cfg, "WP"); if (group) { // Set up control record WPRecPtr = malloc(sizeof(void *)); WPRecPtr[0] = zalloc(sizeof(WPRec)); NumberofWPrecs = 0; while (1) { char Key[16]; char Record[1024]; char * ptr, * ptr2; unsigned int n; sprintf(Key, "R%d", i++); GetStringValue(group, Key, Record); if (Record[0] == 0) // End of List return; memset(&WPRec, 0, sizeof(WPRec)); WP = &WPRec; ptr = Record; ptr2 = strlop(ptr, '|'); if (ptr) strcpy(&WP->callsign[0], ptr); ptr = ptr2; ptr2 = strlop(ptr, '|'); if (ptr) strcpy(&WP->name[0], ptr); ptr = ptr2; ptr2 = strlop(ptr, '|'); if (ptr) WP->Type = atoi(ptr); ptr = ptr2; ptr2 = strlop(ptr, '|'); if (ptr) WP->changed = atoi(ptr); ptr = ptr2; ptr2 = strlop(ptr, '|'); if (ptr) WP->seen = atoi(ptr); ptr = ptr2; ptr2 = strlop(ptr, '|'); if (ptr) strcpy(&WP->first_homebbs[0], ptr); ptr = ptr2; ptr2 = strlop(ptr, '|'); if (ptr) strcpy(&WP->secnd_homebbs[0], ptr); ptr = ptr2; ptr2 = strlop(ptr, '|'); if (ptr) strcpy(&WP->first_zip[0], ptr); ptr = ptr2; ptr2 = strlop(ptr, '|'); if (ptr) strcpy(&WP->secnd_zip[0], ptr); ptr = ptr2; ptr2 = strlop(ptr, '|'); if (ptr == NULL) continue; if (strlen(ptr) > 30) ptr[30] = 0; strcpy(&WP->first_qth[0], ptr); ptr = ptr2; ptr2 = strlop(ptr, '|'); if (ptr == NULL) continue; if (strlen(ptr) > 30) ptr[30] = 0; strcpy(&WP->secnd_qth[0], ptr); ptr = ptr2; ptr2 = strlop(ptr, '|'); if (ptr) WP->last_modif = atol(ptr); ptr = ptr2; ptr2 = strlop(ptr, '|'); if (ptr) { WP->last_seen = atol(ptr); // Check Call for (n = 1; n < strlen(WP->callsign); n++) // skip first which may also be digit { if (isdigit(WP->callsign[n])) { // Has a digit. Check Last is not digit if (isalpha(WP->callsign[strlen(WP->callsign) - 1])) { WP = LookupWP(WPRec.callsign); if (WP == NULL) WP = AllocateWPRecord(); memcpy(WP, &WPRec, sizeof(WPRec)); goto WPOK; } } } Debugprintf("Bad WP Call %s", WP->callsign); } WPOK:; } return; } // If text format exists use it strcpy(CfgName, WPDatabasePath); strlop(CfgName, '.'); strcat(CfgName, ".cfg"); if (stat(CfgName, &STAT) == -1) goto tryOld; config_init(&wpcfg); if (!config_read_file(&wpcfg, CfgName)) { char Msg[256]; sprintf(Msg, "Config File %s Line %d - %s\n", CfgName, config_error_line(&wpcfg), config_error_text(&wpcfg)); printf("%s", Msg); config_destroy(&wpcfg); goto tryOld; } // Set up control record WPRecPtr = malloc(sizeof(void *)); WPRecPtr[0] = zalloc(sizeof(WPRec)); NumberofWPrecs = 0; while (1) { char Key[16]; char Temp[128]; sprintf(Key, "R%d", i++); wpgroup = config_lookup(&wpcfg, Key); if (wpgroup == NULL) // End of List { config_destroy(&wpcfg); return; } memset(&WPRec, 0, sizeof(WPRec)); GetStringValue(wpgroup, "c", WPRec.callsign); GetStringValue(wpgroup, "n", WPRec.name); WPRec.Type = GetIntValue(wpgroup, "T"); WPRec.changed = GetIntValue(wpgroup, "ch"); WPRec.seen = GetIntValue(wpgroup, "s"); GetStringValue(wpgroup, "h", WPRec.first_homebbs); GetStringValue(wpgroup, "sh", WPRec.secnd_homebbs); GetStringValue(wpgroup, "z", WPRec.first_zip); GetStringValue(wpgroup, "sz", WPRec.secnd_zip); GetStringValue(wpgroup, "q", Temp); Temp[30] = 0; strcpy(WPRec.first_qth, Temp); GetStringValue(wpgroup, "sq", Temp); Temp[30] = 0; strcpy(WPRec.secnd_qth, Temp); val = GetIntValue(wpgroup, "m"); WPRec.last_modif = val; val = GetIntValue(wpgroup, "ls"); WPRec.last_seen = val; _strupr(WPRec.callsign); strlop(WPRec.callsign, ' '); if (strlen(WPRec.callsign) > 2) { if (strchr(WPRec.callsign, ':')) continue; if (BadCall(WPRec.callsign)) continue; WP = LookupWP(WPRec.callsign); if (WP == NULL) WP = AllocateWPRecord(); memcpy(WP, &WPRec, sizeof(WPRec)); } } tryOld: Handle = fopen(WPDatabasePath, "rb"); if (Handle == NULL) { // Initialise a new File WPRecPtr = malloc(sizeof(void *)); WPRecPtr[0] = malloc(sizeof(WPRec)); memset(WPRecPtr[0], 0, sizeof(WPRec)); NumberofWPrecs = 0; return; } // Get First Record ReadLen = fread(&WPRec, 1, sizeof(WPRec), Handle); if (ReadLen == 0) { // Duff file memset(&WPRec, 0, sizeof(WPRec)); } // Set up control record WPRecPtr = malloc(sizeof(void *)); WPRecPtr[0] = malloc(sizeof(WPRec)); memcpy(WPRecPtr[0], &WPRec, sizeof(WPRec)); NumberofWPrecs = 0; Next: ReadLen = fread(&WPRec, 1, sizeof(WPRec), Handle); if (ReadLen == sizeof(WPRec)) { _strupr(WPRec.callsign); strlop(WPRec.callsign, ' '); if (strlen(WPRec.callsign) > 2) { if (strchr(WPRec.callsign, ':')) goto Next; if (BadCall(WPRec.callsign)) goto Next; WP = LookupWP(WPRec.callsign); if (WP == NULL) WP = AllocateWPRecord(); memcpy(WP, &WPRec, sizeof(WPRec)); } goto Next; } fclose(Handle); SaveWPDatabase(); } VOID CopyWPDatabase() { char Backup[MAX_PATH]; char Orig[MAX_PATH]; return; strcpy(Backup, WPDatabasePath); strcat(Backup, ".bak"); CopyFile(WPDatabasePath, Backup, FALSE); strcpy(Backup, WPDatabasePath); strlop(Backup, '.'); strcat(Backup, ".cfg.bak"); strcpy(Orig, WPDatabasePath); strlop(Orig, '.'); strcat(Orig, ".cfg"); CopyFile(Orig, Backup, FALSE); } VOID SaveWPDatabase() { // SaveConfig(ConfigName); // WP config is now in main config file int i; config_setting_t *root, *group; config_t cfg; char Key[16]; WPRec * WP; char CfgName[MAX_PATH]; long long val; memset((void *)&cfg, 0, sizeof(config_t)); config_init(&cfg); root = config_root_setting(&cfg); for (i = 0; i <= NumberofWPrecs; i++) { WP = WPRecPtr[i]; sprintf(Key, "R%d", i); group = config_setting_add(root, Key, CONFIG_TYPE_GROUP); SaveStringValue(group, "c", &WP->callsign[0]); SaveStringValue(group, "n", &WP->name[0]); SaveIntValue(group, "T", WP->Type); SaveIntValue(group, "ch", WP->changed); SaveIntValue(group, "s", WP->seen); SaveStringValue(group, "h", &WP->first_homebbs[0]); SaveStringValue(group, "sh", &WP->secnd_homebbs[0]); SaveStringValue(group, "z", &WP->first_zip[0]); SaveStringValue(group, "sz", &WP->secnd_zip[0]); SaveStringValue(group, "q", &WP->first_qth[0]); SaveStringValue(group, "sq", &WP->secnd_qth[0]); val = WP->last_modif; SaveInt64Value(group, "m", val); val = WP->last_seen; SaveInt64Value(group, "ls", val); } strcpy(CfgName, WPDatabasePath); strlop(CfgName, '.'); strcat(CfgName, ".cfg"); Debugprintf("Saving WP Database to %s\n", CfgName); config_write_file(&cfg, CfgName); config_destroy(&cfg); } WPRec * LookupWP(char * Call) { WPRec * ptr = NULL; int i; for (i=1; i <= NumberofWPrecs; i++) { ptr = WPRecPtr[i]; if (_stricmp(ptr->callsign, Call) == 0) return ptr; } return NULL; } char * FormatWPDate(time_t Datim) { struct tm *tm; static char Date[]="xx-xxx-xx "; tm = gmtime(&Datim); if (tm) { if (tm->tm_year >= 100) sprintf_s(Date, sizeof(Date), "%02d-%3s-%02d", tm->tm_mday, month[tm->tm_mon], tm->tm_year - 100); else sprintf_s(Date, sizeof(Date), ""); } return Date; } #ifndef LINBPQ int Do_WP_Sel_Changed(HWND hDlg) { // Update WP display with newly selected rec WPRec * WP; int Sel = SendDlgItemMessage(hDlg, IDC_WP, CB_GETCURSEL, 0, 0); char Type[] = " "; if (Sel == -1) SendDlgItemMessage(hDlg, IDC_WP, WM_GETTEXT, Sel, (LPARAM)(LPCTSTR)&CurrentWPCall); else SendDlgItemMessage(hDlg, IDC_WP, CB_GETLBTEXT, Sel, (LPARAM)(LPCTSTR)&CurrentWPCall); for (CurrentWPIndex = 1; CurrentWPIndex <= NumberofWPrecs; CurrentWPIndex++) { WP = WPRecPtr[CurrentWPIndex]; if (_stricmp(WP->callsign, CurrentWPCall) == 0) { SetDlgItemText(hDlg, IDC_WPNAME, WP->name); SetDlgItemText(hDlg, IDC_HOMEBBS1, WP->first_homebbs); SetDlgItemText(hDlg, IDC_HOMEBBS2, WP->secnd_homebbs); SetDlgItemText(hDlg, IDC_QTH1, WP->first_qth); SetDlgItemText(hDlg, IDC_QTH2, WP->secnd_qth); SetDlgItemText(hDlg, IDC_ZIP1, WP->first_zip); SetDlgItemText(hDlg, IDC_ZIP2, WP->secnd_zip); Type[0] = WP->Type; SetDlgItemText(hDlg, IDC_TYPE, Type); SetDlgItemInt(hDlg, IDC_CHANGED, WP->changed, FALSE); SetDlgItemInt(hDlg, IDC_SEEN, WP->seen, FALSE); SetDlgItemText(hDlg, IDC_LASTSEEN, FormatWPDate(WP->last_seen)); SetDlgItemText(hDlg, IDC_LASTMODIFIED, FormatWPDate(WP->last_modif)); return 0; } } CurrentWPIndex = -1; return 0; } INT_PTR CALLBACK InfoDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); VOID Do_Save_WPRec(HWND hDlg) { WPRec * WP; char Type[] = " "; BOOL OK1; if (CurrentWPIndex == -1) { sprintf(InfoBoxText, "Please select a WP Record to save"); DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); return; } WP = WPRecPtr[CurrentWPIndex]; if (strcmp(CurrentWPCall, WP->callsign) != 0) { sprintf(InfoBoxText, "Inconsistancy detected - record not saved"); DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); return; } GetDlgItemText(hDlg, IDC_WPNAME, WP->name, 13); GetDlgItemText(hDlg, IDC_HOMEBBS1, WP->first_homebbs, 41); GetDlgItemText(hDlg, IDC_HOMEBBS2, WP->secnd_homebbs, 41); GetDlgItemText(hDlg, IDC_QTH1, WP->first_qth, 31); GetDlgItemText(hDlg, IDC_QTH2, WP->secnd_qth, 31); GetDlgItemText(hDlg, IDC_ZIP1, WP->first_zip, 31); GetDlgItemText(hDlg, IDC_ZIP2, WP->secnd_zip, 31); WP->last_modif = time(NULL); WP->seen = GetDlgItemInt(hDlg, IDC_SEEN, &OK1, FALSE); WP->Type = 'U'; WP->changed = 1; SaveWPDatabase(); sprintf(InfoBoxText, "WP information saved"); DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); } VOID Do_Delete_WPRec(HWND hDlg) { WPRec * WP; int n; if (CurrentWPIndex == -1) { sprintf(InfoBoxText, "Please select a WP Record to delete"); DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); return; } WP = WPRecPtr[CurrentWPIndex]; if (strcmp(CurrentWPCall, WP->callsign) != 0) { sprintf(InfoBoxText, "Inconsistancy detected - record not deleted"); DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); return; } for (n = CurrentWPIndex; n < NumberofWPrecs; n++) { WPRecPtr[n] = WPRecPtr[n+1]; // move down all following entries } NumberofWPrecs--; SendDlgItemMessage(hDlg, IDC_WP, CB_RESETCONTENT, 0, 0); for (n = 1; n <= NumberofWPrecs; n++) { SendDlgItemMessage(hDlg, IDC_WP, CB_ADDSTRING, 0, (LPARAM)WPRecPtr[n]->callsign); } sprintf(InfoBoxText, "WP record for %s deleted", WP->callsign); DialogBox(hInst, MAKEINTRESOURCE(IDD_USERADDED_BOX), hWnd, InfoDialogProc); free(WP); SaveWPDatabase(); return; } INT_PTR CALLBACK WPEditDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { int Command, n; UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: for (n = 1; n <= NumberofWPrecs; n++) { SendDlgItemMessage(hDlg, IDC_WP, CB_ADDSTRING, 0, (LPARAM)WPRecPtr[n]->callsign); } return (INT_PTR)TRUE; case WM_CTLCOLORDLG: return (LONG)bgBrush; case WM_CTLCOLORSTATIC: { HDC hdcStatic = (HDC)wParam; SetTextColor(hdcStatic, RGB(0, 0, 0)); SetBkMode(hdcStatic, TRANSPARENT); return (LONG)bgBrush; } case WM_COMMAND: Command = LOWORD(wParam); switch (Command) { case IDOK: case IDCANCEL: EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; case IDC_WP: // Msg Selection Changed Do_WP_Sel_Changed(hDlg); return TRUE; case IDC_SAVEWP: Do_Save_WPRec(hDlg); return TRUE; case IDC_DELETEWP: Do_Delete_WPRec(hDlg); return TRUE; } break; } return (INT_PTR)FALSE; } #endif VOID GetWPBBSInfo(char * Rline) { // Update WP with /I records for each R: Line // R:111206/1636Z 29130@N9PMO.#SEWI.WI.USA.NOAM [Racine, WI] FBB7.00i struct tm rtime; time_t RLineTime; int Age; WPRec * WP; char ATBBS[200]; char Call[200]; char QTH[200] = ""; int RLen; char * ptr1; char * ptr2; memset(&rtime, 0, sizeof(struct tm)); if (Rline[10] == '/') { // Dodgy 4 char year sscanf(&Rline[2], "%04d%02d%02d/%02d%02d", &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); rtime.tm_year -= 1900; rtime.tm_mon--; } else if (Rline[8] == '/') { sscanf(&Rline[2], "%02d%02d%02d/%02d%02d", &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); if (rtime.tm_year < 90) rtime.tm_year += 100; // Range 1990-2089 rtime.tm_mon--; } // Otherwise leave date as zero, which should be rejected if ((RLineTime = mktime(&rtime)) != (time_t)-1 ) { Age = (time(NULL) - RLineTime)/86400; if ( Age < -1) return; // in the future if (Age > BidLifetime || Age > MaxAge) return; // Too old } ptr1 = strchr(Rline, '@'); ptr2 = strchr(Rline, '\r'); if (!ptr1) return; // Duff if (*++ptr1 == ':') ptr1++; // Format 2 if (ptr2 == NULL) return; // No CR on end RLen = ptr2 - ptr1; if (RLen > 200) return; memcpy(ATBBS, ptr1, RLen); ATBBS[RLen] = 0; ptr2 = strchr(ATBBS, ' '); if (ptr2) *ptr2 = 0; strcpy(Call, ATBBS); strlop(Call, '.'); ptr2 = memchr(ptr1, '[', RLen); if (ptr2) { ptr1= memchr(ptr2, ']', RLen); if (ptr1) memcpy(QTH, ptr2 + 1, ptr1 - ptr2 - 1); } if (BadCall(Call)) return; WP = LookupWP(Call); if (!WP) { // Not Found WP = AllocateWPRecord(); strcpy(WP->callsign, Call); strcpy(WP->first_homebbs, ATBBS); strcpy(WP->secnd_homebbs, ATBBS); if (QTH[0]) { strcpy(WP->first_qth, QTH); strcpy(WP->secnd_qth, QTH); } WP->last_modif = RLineTime; WP->last_seen = RLineTime; WP->Type = 'I'; WP->changed = TRUE; return; } if (WP->last_modif >= RLineTime || WP->Type != 'I') return; // Update 2nd if changed if (strcmp(WP->secnd_homebbs , ATBBS) != 0) { strcpy(WP->secnd_homebbs, ATBBS); WP->last_modif = RLineTime; } if (QTH[0] && strcmp(WP->secnd_qth , QTH) != 0) { strcpy(WP->secnd_qth, QTH); WP->last_modif = RLineTime; } return; } VOID GetWPInfoFromRLine(char * From, char * FirstRLine, time_t RLineTime) { /* The /G suffix denotes that the information in this line has been gathered by examining the header of a message to GUESS at which BBS the sender is registered. The HomeBBS of the User is assumed to be the BBS shown in the first R: header line. The date associated with this information is the date shown on this R: header line. */ // R:930101/0000 1530@KA6FUB.#NOCAL.CA.USA.NOAM // R:930101/0000 @:KA6FUB.#NOCAL.CA.USA.NOAM #:1530 // The FirstRLine pointer points to the message, so shouldnt be changed WPRec * WP; char ATBBS[200]; int RLen; char * ptr1 = strchr(FirstRLine, '@'); char * ptr2 = strchr(FirstRLine, '\r'); if (BadCall(From)) return; if (!ptr1) return; // Duff if (*++ptr1 == ':') ptr1++; // Format 2 RLen = ptr2 - ptr1; if (RLen > 200) return; memcpy(ATBBS, ptr1, RLen); ATBBS[RLen] = 0; ptr2 = strchr(ATBBS, ' '); if (ptr2) *ptr2 = 0; if (strlen(ATBBS) > 40) ATBBS[40] = 0; WP = LookupWP(From); if (!WP) { // Not Found WP = AllocateWPRecord(); strcpy(WP->callsign, From); strcpy(WP->first_homebbs, ATBBS); strcpy(WP->secnd_homebbs, ATBBS); WP->last_modif = RLineTime; WP->last_seen = RLineTime; WP->Type = 'G'; WP->changed = TRUE; return; } if (WP->last_modif >= RLineTime) return; // Update 2nd if changed if (strcmp(WP->secnd_homebbs , ATBBS) != 0) { strcpy(WP->secnd_homebbs, ATBBS); WP->last_modif = RLineTime; } return; } VOID ProcessWPMsg(char * MailBuffer, int Size, char * FirstRLine) { char * ptr1 = MailBuffer; char * ptr2; WPRec * WP; char WPLine[200]; int WPLen; ptr1[Size] = 0; ptr1 = FirstRLine; if (ptr1 == NULL) return; while (*ptr1) { ptr2 = strchr(ptr1, '\r'); if (ptr2 == 0) // No CR in buffer return; WPLen = ptr2 - ptr1; if ((memcmp(ptr1, "On ", 3) == 0) && (WPLen < 200)) { char * Date; char * Call; char * AT; char * HA; char * zip; char * ZIP; char * Name; char * QTH = NULL; char * Context; char seps[] = " \r"; // Make copy of string, as strtok messes with it memcpy(WPLine, ptr1, WPLen); WPLine[WPLen] = 0; Date = strtok_s(WPLine+3, seps, &Context); Call = strtok_s(NULL, seps, &Context); AT = strtok_s(NULL, seps, &Context); HA = strtok_s(NULL, seps, &Context); zip = strtok_s(NULL, seps, &Context); ZIP = strtok_s(NULL, seps, &Context); Name = strtok_s(NULL, seps, &Context); QTH = strtok_s(NULL, "\r", &Context); // QTH may have spaces if (Date == 0 || Call == 0 || AT == 0 || ZIP == 0 || Name == 0 || QTH == 0) return; if (strlen(HA) > 40) return; if (strlen(ZIP) > 8) return; if (strlen(Name) > 12) return; if (strlen(QTH) > 30) return; if (AT[0] == '@' && (QTH)) { struct tm rtime; time_t WPDate; char Type; char * TypeString; if (memcmp(Name, "?", 2) == 0) Name = NULL; if (memcmp(ZIP, "?", 2) == 0) ZIP = NULL; if (memcmp(QTH, "?", 2) == 0) QTH = NULL; memset(&rtime, 0, sizeof(struct tm)); sscanf(Date, "%02d%02d%02d", &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday); rtime.tm_year += 100; rtime.tm_mon--; /* This process freshens the database, following receipt of the new or changed information detailed above. The update subroutine will first look for an entry in the database for the callsign which matches the received information. If it does not exist then a completely new record will be created in the database and the information be used to fill what fields it can, in both the active and the temporary components. The date will be then changed to the one associated with the update information. If the record does already exist, then the unknown fields of both the temporary and active fields will be filled in, and those fields already known in the temporary part will be replaced by the new information if the date new information is younger than that already on file. The date will then be adjusted such that it is consistent with the updated information. If the new information is of the /U category, then the current fields will be replaced by the new information in both the primary and secondary (Active and Temporary) parts of the record, as this information has been input directly from the user. If the information was of another category then only the secondary (Temporary) part of the record will be updated, so the Active or primary record will remain unchanged at this time. If a field is changed, a flag giving the update request type is then validated. If the /U flag is already validated, it will not be replaced. This flag will be used in case the WP update messages are validated. */ if ((WPDate = mktime(&rtime)) != (time_t)-1 ) { WPDate -= (time_t)_MYTIMEZONE; TypeString = strlop(Call, '/'); if (strlen(Call) < 3 || strlen(Call) > 9) return; if (TypeString) Type = TypeString[0]; else Type = 'G'; if (strchr(Call, ':')) break; if (BadCall(Call)) break; WP = LookupWP(Call); if (WP) { // Found, so update DoWPUpdate(WP, Type, Name, HA, QTH, ZIP, WPDate); } else { WP = AllocateWPRecord(); strcpy(WP->callsign, Call); if (Name) strcpy(WP->name, Name); strcpy(WP->first_homebbs, HA); strcpy(WP->secnd_homebbs, HA); if (QTH) { strcpy(WP->first_qth, QTH); strcpy(WP->secnd_qth, QTH);; } if (ZIP) { strcpy(WP->first_zip, ZIP); strcpy(WP->secnd_zip, ZIP); } WP->Type = Type; WP->last_modif = WPDate; WP->last_seen = WPDate; WP->changed = TRUE; WP->seen++; } } } } ptr1 = ++ptr2; if (*ptr1 == '\n') ptr1++; } SaveWPDatabase(); return; } VOID DoWPUpdate(WPRec * WP, char Type, char * Name, char * HA, char * QTH, char * ZIP, time_t WPDate) { // First Update any unknown field if(Name) if (WP->name == NULL) {strcpy(WP->name, Name); WP->last_modif = WPDate; WP->changed = TRUE;} if (QTH) { if (WP->first_qth == NULL) {strcpy(WP->first_qth, QTH); WP->last_modif = WPDate; WP->changed = TRUE;} if (WP->secnd_qth == NULL) {strcpy(WP->secnd_qth, QTH); WP->last_modif = WPDate; WP->changed = TRUE;} } if (ZIP) { if (WP->first_zip == NULL) {strcpy(WP->first_zip, ZIP); WP->last_modif = WPDate; WP->changed = TRUE;} if (WP->secnd_zip == NULL) {strcpy(WP->secnd_zip, ZIP); WP->last_modif = WPDate; WP->changed = TRUE;} } WP->last_seen = WPDate; WP->seen++; // Now Update Temp Fields if update is newer than original if (WP->last_modif >= WPDate) return; if (Type == 'U') // Definitive Update { if (strcmp(WP->first_homebbs , HA) != 0) { strcpy(WP->first_homebbs, HA); strcpy(WP->secnd_homebbs, HA); WP->last_modif = WPDate; WP->changed = TRUE; } if (Name) { if (strcmp(WP->name , Name) != 0) { strcpy(WP->name, Name); WP->last_modif = WPDate; WP->changed = TRUE; } } if (QTH) { if (strcmp(WP->first_qth , QTH) != 0) { strcpy(WP->first_qth, QTH); strcpy(WP->secnd_qth, QTH); WP->last_modif = WPDate; WP->changed = TRUE; } } if (ZIP) { if (strcmp(WP->first_zip , ZIP) != 0) { strcpy(WP->first_zip, ZIP); strcpy(WP->secnd_zip, ZIP); WP->last_modif = WPDate; WP->changed = TRUE; } } WP->Type = Type; return; } // Non-Definitive - only update second copy if (strcmp(WP->secnd_homebbs , HA) != 0) {strcpy(WP->secnd_homebbs, HA); WP->last_modif = WPDate; WP->Type = Type;} if (Name) if (strcmp(WP->name , Name) != 0) {strcpy(WP->name, Name); WP->last_modif = WPDate; WP->Type = Type;} if (QTH) if (strcmp(WP->secnd_qth , QTH) != 0) {strcpy(WP->secnd_qth, QTH); WP->last_modif = WPDate; WP->Type = Type;} if (ZIP) if (strcmp(WP->secnd_zip , ZIP) != 0) {strcpy(WP->secnd_zip, ZIP); WP->last_modif = WPDate; WP->Type = Type;} return; } VOID UpdateWPWithUserInfo(struct UserInfo * user) { WPRec * WP = LookupWP(user->Call); if (strchr(user->Call, ':')) return; if (BadCall(user->Call)) return; if (!WP) { WP = AllocateWPRecord(); strcpy(WP->callsign, user->Call); } // Update Record if (user->HomeBBS[0]) { strcpy(WP->first_homebbs, user->HomeBBS); strcpy(WP->secnd_homebbs, user->HomeBBS); } if (user->Address[0]) { char Temp[127]; strcpy(Temp, user->Address); Temp[30] = 0; strcpy(WP->first_qth, Temp); strcpy(WP->secnd_qth, Temp); } if (user->ZIP[0]) { strcpy(WP->first_zip, user->ZIP); strcpy(WP->secnd_zip, user->ZIP ); } if (user->Name[0]) strcpy(WP->name, user->Name); WP->last_modif = WP->last_seen = time(NULL); WP->changed = TRUE; WP->Type = 'U'; SaveWPDatabase(); } VOID DoWPLookup(ConnectionInfo * conn, struct UserInfo * user, char Type, char *Param) { // Process the I call command WPRec * ptr = NULL; int i; char ATBBS[100]; char * HA; char * Rest; _strupr(Param); Type = toupper(Type); switch (Type) { case 0: for (i=1; i <= NumberofWPrecs; i++) { ptr = WPRecPtr[i]; if (wildcardcompare(ptr->callsign, Param)) { nodeprintf(conn, "%s %s %s %s %s\r", ptr->callsign, ptr->first_homebbs, ptr->name, ptr->first_zip, ptr->first_qth); } } return; case'@': // AT BBS for (i=1; i <= NumberofWPrecs; i++) { ptr = WPRecPtr[i]; strcpy(ATBBS, ptr->first_homebbs); strlop(ATBBS,'.'); if (wildcardcompare(ATBBS, Param)) { nodeprintf(conn, "%s %s %s %s %s\r", ptr->callsign, ptr->first_homebbs, ptr->name, ptr->first_zip, ptr->first_qth); } } case'H': // Hierarchic Element for (i=1; i <= NumberofWPrecs; i++) { ptr = WPRecPtr[i]; strcpy(ATBBS, ptr->first_homebbs); Rest = strlop(ATBBS,'.'); if (Rest == 0) continue; HA = strtok_s(Rest, ".", &Rest); while (HA) { if (wildcardcompare(HA, Param)) { nodeprintf(conn, "%s %s %s %s %s\r", ptr->callsign, ptr->first_homebbs, ptr->name, ptr->first_zip, ptr->first_qth); } HA = strtok_s(NULL, ".", &Rest); } } return; case'Z': // ZIP for (i=1; i <= NumberofWPrecs; i++) { ptr = WPRecPtr[i]; if (ptr->first_zip[0] == 0) continue; if (wildcardcompare(ptr->first_zip, Param)) { nodeprintf(conn, "%s %s %s %s %s\r", ptr->callsign, ptr->first_homebbs, ptr->name, ptr->first_zip, ptr->first_qth); } } return; } nodeprintf(conn, "Invalid I command option %c\r", Type); } /* On 111120 N4ZKF/I @ N4ZKF.#NFL.FL.USA.NOAM zip 32118 Dave 32955 On 111120 F6IQF/I @ F6IQF.FRPA.FRA.EU zip ? ? f6iqf.dyndns.org On 111121 W9JUN/I @ W9JUN.W9JUN.#SEIN.IN.US.NOAM zip 47250 Don NORTH VERNON, IN On 111120 KR8ZY/U @ KR8ZY zip ? john ? On 111120 N0DEC/G @ N0ARY.#NCA.CA.USA.NOAM zip ? ? ? From: N0JAL To: WP Type/Status: B$ Date/Time: 22-Nov 10:15Z Bid: 95F7N0JAL Title: WP Update R:111122/1500Z 35946@KD6PGI.OR.USA.NOAM BPQ1.0.4 R:111122/1020 16295@K7ZS.OR.USA.NOAM R:111122/1015 38391@N0JAL.OR.USA.NOAM On 111121 N0JAL @ N0JAL.OR.USA.NOAM zip ? Sai Damascus, Oregon CN85sj */ VOID UpdateWP() { // If 2nd copy of info has been unchanged for 30 days, copy to active fields WPRec * ptr = NULL; int i; char * via = NULL; int MsgLen = 0; char MailBuffer[100100]; char * Buffptr = MailBuffer; int WriteLen=0; char HDest[61] = "WP"; char WPMsgType = 'P'; time_t NOW = time(NULL); time_t UpdateLimit = NOW - (86400 * 30); // 30 days LASTWPSendTime = NOW - (86400 * 5); // 5 days max for (i=1; i <= NumberofWPrecs; i++) { ptr = WPRecPtr[i]; // DO we have a new field, and if so is it different? if ((ptr->secnd_homebbs[0] && _stricmp(ptr->first_homebbs, ptr->secnd_homebbs)) || (ptr->secnd_qth[0] && _stricmp(ptr->first_qth, ptr->secnd_qth)) || (ptr->secnd_zip[0] && _stricmp(ptr->first_zip, ptr->secnd_zip))) { // Have new data if (ptr->last_modif < UpdateLimit) { // Stable for 30 days if (ptr->secnd_homebbs[0]) strcpy(ptr->first_homebbs, ptr->secnd_homebbs); if (ptr->secnd_qth[0]) strcpy(ptr->first_qth, ptr->secnd_qth); if (ptr->secnd_zip[0]) strcpy(ptr->first_zip, ptr->secnd_zip); ptr->last_modif = NOW; } } } } int CreateWPMessage() { // Include all messages with Type of U whach have changed since LASTWPSendTime WPRec * ptr = NULL; int i; struct tm *tm; struct MsgInfo * Msg; char * via = NULL; char BID[13]; BIDRec * BIDRec; int MsgLen = 0; char MailBuffer[100100]; char * Buffptr = MailBuffer; char MsgFile[MAX_PATH]; FILE * hFile; int WriteLen=0; char ** To = SendWPAddrs; LASTWPSendTime = time(NULL) - (86400 * 5); // 5 days max for (i=1; i <= NumberofWPrecs; i++) { ptr = WPRecPtr[i]; // if (ptr->last_modif > LASTWPSendTime && ptr->Type == 'U' && ptr->first_homebbs[0]) if (ptr->changed && ptr->last_modif > LASTWPSendTime && ptr->first_homebbs[0]) { tm = gmtime((time_t *)&ptr->last_modif); MsgLen += sprintf(Buffptr, "On %02d%02d%02d %s/%c @ %s zip %s %s %s\r\n", tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, ptr->callsign, ptr->Type, ptr->first_homebbs, (ptr->first_zip[0]) ? ptr->first_zip : "?", (ptr->name[0]) ? ptr->name : "?", (ptr->first_qth[0]) ? ptr->first_qth : "?"); Buffptr = &MailBuffer[MsgLen]; ptr->changed = FALSE; if (MsgLen > 100000) break; } } if (MsgLen == 0) return TRUE; while(To[0]) { char TO[256]; char * VIA; Msg = AllocateMsgRecord(); // Set number here so they remain in sequence Msg->number = ++LatestMsg; Msg->length = MsgLen; MsgnotoMsg[Msg->number] = Msg; strcpy(Msg->from, BBSName); strcpy(TO, To[0]); VIA = strlop(TO, '@'); if (VIA) { if (strlen(VIA) > 40) VIA[40] = 0; strcpy(Msg->via, VIA); } strcpy(Msg->to, TO); strcpy(Msg->title, "WP Update"); Msg->type = (SendWPType) ? 'P' : 'B'; Msg->status = 'N'; sprintf_s(BID, sizeof(BID), "%d_%s", LatestMsg, BBSName); strcpy(Msg->bid, 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); sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); hFile = fopen(MsgFile, "wb"); if (hFile) { fwrite(MailBuffer, 1, Msg->length, hFile); fclose(hFile); } MatchMessagetoBBSList(Msg, 0); BuildNNTPList(Msg); // Build NNTP Groups list #ifndef NOMQTT if (MQTT) MQTTMessageEvent(Msg); #endif To++; } SaveMessageDatabase(); SaveBIDDatabase(); return TRUE; } VOID CreateWPReport() { int i; char Line[200]; int len; char File[100]; FILE * hFile; WPRec * WP = NULL; sprintf_s(File, sizeof(File), "%s/wp.txt", BaseDir); hFile = fopen(File, "wb"); if (hFile == NULL) return; // len = sprintf(Line, " Call Connects Connects Messages Messages Bytes Bytes Rejected Rejected\r\n"); // WriteFile(hFile, Line, len, &written, NULL); // len = sprintf(Line, " In Out Rxed Sent Rxed Sent In Out\r\n\r\n"); // WriteFile(hFile, Line, len, &written, NULL); for (i=1; i <= NumberofWPrecs; i++) { WP = WPRecPtr[i]; len = sprintf(Line, "%-7s,%c,%s,%s,%s,%s,%s,%s,%s,%d,%s,%s\r\n", WP->callsign, WP->Type, WP->first_homebbs, WP->first_qth, WP->first_zip, WP->secnd_homebbs, WP->secnd_qth, WP->secnd_zip, WP->name, WP->changed, FormatWPDate((time_t)WP->last_modif), FormatWPDate((time_t)WP->last_seen)); fwrite(Line, 1, len, hFile); } fclose(hFile); }