/* 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 */ // Mail and Chat Server for BPQ32 Packet Switch // // Message Routing Module // This code decides where to send a message. // Private messages are routed by TO and AT if possible, if not they follow the Bull Rules // Bulls are routed on HA where possible // Bulls should not be distributed outside their designated area. // Use 4 char continent codes if this is set int FOURCHARCONT = 0; #include "bpqmail.h" char WW[] = "WW"; char * MyElements[20] = {WW}; // My HA in element format char MyRouteElements[100]; int MyElementCount; BOOL ReaddressLocal = 0; BOOL ReaddressReceived = 0; BOOL WarnNoRoute = TRUE; BOOL SendPtoMultiple = FALSE; BOOL Localtime = FALSE; // Use Local Time for Timebands and forward connect scripts struct ALIAS * CheckForNTSAlias(struct MsgInfo * Msg, char * FirstDestElement); struct UserInfo * FindAMPR(); struct UserInfo * FindBBS(char * Name); struct Continent { char FourCharCode[5]; char TwoCharCode[5]; }; struct Country { char Country[5]; char Continent4[5]; char Continent2[3]; }; struct Continent Continents[] = { "EURO", "EU", // Europe "MEDR", "EU", // Mediterranean "ASIA", "AS", // The Orient "INDI", "AS", // Indian Ocean including the Indian subcontinent "MDLE", "AS", // Middle East "SEAS", "AS", // South-East Asia "NOAM", "NA", // North America (Canada, USA, Mexico) "CEAM", "NA", // Central America "CARB", "NA", // Caribbean "SOAM", "SA", // South America "AUNZ", "OC", // Australia/New Zealand "EPAC", "OC", // Eastern Pacific "NPAC", "OC", // Northern Pacific "SPAC", "OC", // Southern Pacific "WPAC", "OC", // Western Pacific "NAFR", "AF", // Northern Africa "CAFR", "AF", // Central Africa "SAFR", "AF", // Southern Africa "ANTR", "OC", // Antarctica "MARS", "MARS", // Special for MARS network }; struct Country Countries[] = { "AFG", "ASIA", "AS", // Afghanistan "ALA", "EURO", "EU", // Åland Islands "ALB", "EURO", "EU", // Albania "DZA", "NAFR", "AF", // Algeria "ASM", "ASIA", "AS", // American Samoa "AND", "EURO", "EU", // Andorra "AGO", "CAFR", "AF", // Angola "AIA", "CARB", "NA", // Anguilla "ATG", "CARB", "NA", // Antigua and Barbuda "ARG", "SOAM", "SA", // Argentina "ARM", "ASIA", "AS", // Armenia "ABW", "CARB", "NA", // Aruba "AUS", "AUNZ", "OC", // Australia "AUT", "EURO", "EU", // Austria "AZE", "ASIA", "AS", // Azerbaijan "BHS", "CARB", "NA", // Bahamas "BHR", "MDLE", "AS", // Bahrain "BGD", "INDE", "AS", // Bangladesh "BRB", "CARB", "NA", // Barbados "BLR", "EURO", "EU", // Belarus "BEL", "EURO", "EU", // Belgium "BLZ", "CEAM", "NA", // Belize "BEN", "CAFR", "AF", // Benin "BMU", "CARB", "NA", // Bermuda "BTN", "ASIA", "AS", // Bhutan "BOL", "SOAM", "SA", // Bolivia (Plurinational State of) "BIH", "EURO", "EU", // Bosnia and Herzegovina "BWA", "SAFR", "AF", // Botswana "BRA", "SOAM", "SA", // Brazil "VGB", "CARB", "NA", // British Virgin Islands "BRN", "ASIA", "AS", // Brunei Darussalam "BGR", "EURO", "EU", // Bulgaria "BFA", "CAFR", "AF", // Burkina Faso "BDI", "CAFR", "AF", // Burundi "KHM", "ASIA", "AS", // Cambodia "CMR", "CAFR", "AF", // Cameroon "CAN", "NOAM", "NA", // Canada "CPV", "NAFR", "AF", // Cape Verde "CYM", "CARB", "NA", // Cayman Islands "CAF", "CAFR", "AF", // Central African Republic "TCD", "CAFR", "AF", // Chad "CHL", "SOAM", "SA", // Chile "CHN", "ASIA", "AS", // China "HKG", "ASIA", "AS", // Hong Kong Special Administrative Region of China "MAC", "ASIA", "AS", // Macao Special Administrative Region of China "COL", "ASIA", "SA", // Colombia // "COM", "SAFR", "AF", // Comoros "COG", "CAFR", "AF", // Congo "COK", "SPAC", "OC", // Cook Islands "CRI", "CEAM", "NA", // Costa Rica "CIV", "CAFR", "AF", // Côte d'Ivoire "HRV", "EURO", "EU", // Croatia "CUB", "CARB", "NA", // Cuba "CYP", "EURO", "EU", // Cyprus "CZE", "EURO", "EU", // Czech Republic "PRK", "ASIA", "AS", // Democratic People's Republic of Korea "COD", "CAFR", "AF", // Democratic Republic of the Congo "DNK", "EURO", "EU", // Denmark "DJI", "NAFR", "AF", // Djibouti "DMA", "CARB", "NA", // Dominica "DOM", "CARB", "NA", // Dominican Republic "ECU", "SOAM", "SA", // Ecuador "EGY", "MDLE", "AF", // Egypt "SLV", "CEAM", "NA", // El Salvador "GNQ", "CAFR", "AF", // Equatorial Guinea "ERI", "NAFR", "AF", // Eritrea "EST", "EURO", "EU", // Estonia "ETH", "NAFR", "AF", // Ethiopia "FRO", "EURO", "EU", // Faeroe Islands "FLK", "SOAM", "SA", // Falkland Islands (Malvinas) "FJI", "SPAC", "OC", // Fiji "FIN", "EURO", "EU", // Finland "FRA", "EURO", "EU", // France "GUF", "SOAM", "SA", // French Guiana "PYF", "SPAC", "OC", // French Polynesia "GAB", "CAFR", "AF", // Gabon "GMB", "CAFR", "AF", // Gambia "GEO", "ASIA", "AS", // Georgia "DEU", "EURO", "EU", // Germany "GHA", "CAFR", "AF", // Ghana "GIB", "EURO", "EU", // Gibraltar "GRC", "EURO", "EU", // Greece "GRL", "EURO", "EU", // Greenland "GRD", "CARB", "NA", // Grenada "GLP", "CARB", "NA", // Guadeloupe "GUM", "SPAC", "OC", // Guam "GTM", "CEAM", "NA", // Guatemala "GGY", "EURO", "EU", // Guernsey "GIN", "CAFR", "AF", // Guinea "GNB", "CAFR", "AF", // Guinea-Bissau "GUY", "SOAM", "SA", // Guyana "HTI", "CARB", "NA", // Haiti "VAT", "EURO", "EU", // Holy See "HND", "CEAM", "NA", // Honduras "HUN", "EURO", "EU", // Hungary "ISL", "EURO", "EU", // Iceland "IND", "INDI", "AS", // India "IDN", "ASIA", "AS", // Indonesia "IRN", "MDLE", "AS", // Iran (Islamic Republic of) "IRQ", "MDLE", "AS", // Iraq "IRL", "EURO", "EU", // Ireland "IMN", "EURO", "EU", // Isle of Man "ISR", "MDLE", "AS", // Israel "ITA", "EURO", "EU", // Italy "JAM", "CEAM", "NA", // Jamaica "JPN", "ASIA", "AS", // Japan "JEY", "EURO", "EU", // Jersey "JOR", "MDLE", "AS", // Jordan "KAZ", "ASIA", "AS", // Kazakhstan "KEN", "CAFR", "AF", // Kenya "KIR", "EPAC", "OC", // Kiribati "KWT", "MDLE", "AS", // Kuwait "KGZ", "ASIA", "AS", // Kyrgyzstan "LAO", "ASIA", "AS", // Lao People's Democratic Republic "LVA", "EURO", "EU", // Latvia "LBN", "MDLE", "AS", // Lebanon "LSO", "SAFR", "AF", // Lesotho "LBR", "CAFR", "AF", // Liberia "LBY", "MDLE", "AS", // Libyan Arab Jamahiriya "LIE", "EURO", "EU", // Liechtenstein "LTU", "EURO", "EU", // Lithuania "LUX", "EURO", "EU", // Luxembourg "MDG", "SAFR", "AF", // Madagascar "MWI", "SAFR", "AF", // Malawi "MYS", "ASIA", "AS", // Malaysia "MDV", "INDI", "AS", // Maldives "MLI", "CAFR", "AF", // Mali "MLT", "EURO", "EU", // Malta "MHL", "WPAC", "OC", // Marshall Islands "MTQ", "CARB", "NA", // Martinique "MRT", "NAFR", "AF", // Mauritania "MUS", "SAFR", "AF", // Mauritius "MYT", "SAFR", "AF", // Mayotte "MEX", "NOAM", "NA", // Mexico "FSM", "WPAC", "OC", // Micronesia (Federated States of) "MCO", "EURO", "EU", // Monaco "MNG", "ASIA", "AS", // Mongolia "MNE", "EURO", "EU", // Montenegro "MSR", "CARB", "NA", // Montserrat "MAR", "NAFR", "AF", // Morocco "MOZ", "SAFR", "AF", // Mozambique "MMR", "ASIA", "AS", // Myanmar "NAM", "SAFR", "AF", // Namibia "NRU", "WPAC", "OC", // Nauru "NPL", "ASIA", "AS", // Nepal "NLD", "EURO", "EU", // Netherlands "ANT", "CARB", "NA", // Netherlands Antilles "NCL", "SPAC", "OC", // New Caledonia "NZL", "AUNZ", "OC", // New Zealand "NIC", "CEAM", "SA", // Nicaragua "NER", "NAFR", "AF", // Niger "NGA", "CAFR", "AF", // Nigeria "NIU", "SPAC", "OC", // Niue "NFK", "SPAC", "OC", // Norfolk Island "MNP", "WPAC", "OC", // Northern Mariana Islands "NOR", "EURO", "EU", // Norway "PSE", "MDLE", "AS", // Occupied Palestinian Territory "OMN", "MDLE", "AS", // Oman "PAK", "INDI", "AS", // Pakistan "PLW", "SPAC", "OC", // Palau "PAN", "CEAM", "SA", // Panama "PNG", "SPAC", "OC", // Papua New Guinea "PRY", "SOAM", "SA", // Paraguay "PER", "SOAM", "SA", // Peru "PHL", "ASIA", "AS", // Philippines "PCN", "SPAC", "OC", // Pitcairn "POL", "EURO", "EU", // Poland "PRT", "EURO", "EU", // Portugal "PRI", "CARB", "NA", // Puerto Rico "QAT", "MDLE", "AS", // Qatar "KOR", "ASIA", "AS", // Republic of Korea "MDA", "EURO", "EU", // Republic of Moldova "REU", "SAFR", "AF", // Réunion "ROU", "EURO", "EU", // Romania "RUS", "ASIA", "AS", // Russian Federation "RWA", "CAFR", "AF", // Rwanda "BLM", "CARB", "NA", // Saint-Barthélemy "SHN", "SOAM", "SA", // Saint Helena "KNA", "CARB", "NA", // Saint Kitts and Nevis "LCA", "CARB", "NA", // Saint Lucia "MAF", "CARB", "NA", // Saint-Martin (French part) "SPM", "NOAM", "NA", // Saint Pierre and Miquelon "VCT", "CARB", "NA", // Saint Vincent and the Grenadines "WSM", "SPAC", "OC", // Samoa "SMR", "EURO", "EU", // San Marino "STP", "CAFR", "AF", // Sao Tome and Principe "SAU", "MDLE", "AS", // Saudi Arabia "SEN", "CAFR", "AF", // Senegal "SRB", "EURO", "EU", // Serbia "SYC", "SAFR", "AF", // Seychelles "SLE", "NAFR", "AF", // Sierra Leone "SGP", "ASIA", "AS", // Singapore "SVK", "EURO", "EU", // Slovakia "SVN", "EURO", "EU", // Slovenia "SLB", "SPAC", "OC", // Solomon Islands "SOM", "NAFR", "AF", // Somalia "ZAF", "SAFR", "AF", // South Africa "ESP", "EURO", "EU", // Spain "LKA", "INDE", "AS", // Sri Lanka "SDN", "NAFR", "AF", // Sudan "SUR", "SOAM", "SA", // Suriname "SJM", "EURO", "EU", // Svalbard and Jan Mayen Islands "SWZ", "SAFR", "AF", // Swaziland "SWE", "EURO", "EU", // Sweden "CHE", "EURO", "EU", // Switzerland "SYR", "MDLE", "AS", // Syrian Arab Republic "TJK", "ASIA", "AS", // Tajikistan "THA", "ASIA", "AS", // Thailand "MKD", "EURO", "EU", // The former Yugoslav Republic of Macedonia "TLS", "ASIA", "AS", // Timor-Leste "TGO", "CAFR", "AF", // Togo "TKL", "AUNZ", "OC", // Tokelau "TON", "SPAC", "OC", // Tonga "TTO", "CARB", "NA", // Trinidad and Tobago "TUN", "NAFR", "AF", // Tunisia "TUR", "EURO", "EU", // Turkey "TKM", "ASIA", "AS", // Turkmenistan "TCA", "CARB", "NA", // Turks and Caicos Islands "TUV", "SPAC", "OC", // Tuvalu "UGA", "SAFR", "AF", // Uganda "UKR", "EURO", "EU", // Ukraine "ARE", "MDLE", "AS", // United Arab Emirates "GBR", "EURO", "EU", // United Kingdom of Great Britain and Northern Ireland "TZA", "SAFR", "AF", // United Republic of Tanzania "USA", "NOAM", "NA", // United States of America "VIR", "CARB", "NA", // United States Virgin Islands "URY", "SOAM", "SA", // Uruguay "UZB", "ASIA", "AS", // Uzbekistan "VUT", "SPAC", "OC", // Vanuatu "VEN", "SOAM", "SA", // Venezuela (Bolivarian Republic of) "VNM", "ASIA", "AS", // Viet Nam "WLF", "SPAC", "OC", // Wallis and Futuna Islands "ESH", "CAFR", "AF", // Western Sahara "YEM", "NAFR", "AF", // Yemen "ZMB", "SAFR", "AF", // Zambia "ZWE", "SAFR", "AF" // Zimbabwe }; char ** AliasText; struct ALIAS ** Aliases; struct ALIAS ** NTSAliases = NULL; /*struct ALIAS Aliases[] = { "AMSAT", "WW", "USBBS", "USA", "ALLUS", "USA"}; */ int NumberofContinents = sizeof(Continents)/sizeof(Continents[1]); int NumberofCountries = sizeof(Countries)/sizeof(Countries[1]); struct Continent * FindContinent(char * Name) { int i; struct Continent * Cont; for(i=0; i< NumberofContinents; i++) { Cont = &Continents[i]; if ((_stricmp(Name, Cont->FourCharCode) == 0) || (_stricmp(Name, Cont->TwoCharCode) == 0)) return Cont; } return NULL; } struct Country * FindCountry(char * Name) { int i; struct Country * Cont; for(i=0; i< NumberofCountries; i++) { Cont = &Countries[i]; if (_stricmp(Name, Cont->Country) == 0) return Cont; } return NULL; } struct ALIAS * FindAlias(char * Name) { struct ALIAS ** Alias; Alias = Aliases; while(Alias[0]) { if (_stricmp(Name, Alias[0]->Dest) == 0) return Alias[0]; Alias++; } return NULL; } VOID SetupMyHA() { int Elements = 1; // Implied WW on front char * ptr2; struct Continent * Continent; strcpy(MyRouteElements, HRoute); // Split it up ptr2 = MyRouteElements + strlen(MyRouteElements) - 1; if (HRoute[0]) { do { while ((*ptr2 != '.') && (ptr2 > MyRouteElements)) ptr2 --; if (ptr2 == MyRouteElements) { // End MyElements[Elements++] = _strdup(ptr2); break; } MyElements[Elements++] = _strdup(ptr2+1); *ptr2 = 0; } while(Elements < 19); // Just in case! } MyElements[Elements++] = _strdup(BBSName); MyElementCount = Elements; if (MyElements[1]) { if (FOURCHARCONT == 0) { if (strlen(MyElements[1]) == 4) { // Convert to 2 char Continent; Continent = FindContinent(MyElements[1]); if (Continent) { free(MyElements[1]); MyElements[1] = _strdup(Continent->TwoCharCode); } } } else { if (strlen(MyElements[1]) == 2) { // Convert to 4 char Continent; Continent = FindContinent(MyElements[1]); if (Continent) { free(MyElements[1]); MyElements[1] = _strdup(Continent->FourCharCode); } } } } } VOID SetupNTSAliases(char * FN) { FILE *in; char Buffer[2048]; char *buf = Buffer; int Count = 0; char seps[] = " ,:/\t"; char * Dest, * Alias, * Context; NTSAliases = zalloc(sizeof(struct ALIAS)); in = fopen(FN, "r"); if (in) { while(fgets(buf, 128, in)) { strlop(buf, '\n'); strlop(buf, '\r'); // in case Windows Format Dest = _strdup(buf); // strtok changes it Dest = strtok_s(Dest, seps, &Context); if (Dest == NULL) continue; Alias = strtok_s(NULL, seps, &Context); if (Alias == NULL) continue; if (strlen(Dest) < 3 && (strchr(Dest, '*') == 0)) continue; NTSAliases = realloc(NTSAliases, (Count+2)* sizeof(struct ALIAS)); NTSAliases[Count] = zalloc(sizeof(struct ALIAS)); NTSAliases[Count]->Dest = Dest; NTSAliases[Count]->Alias = Alias; Count++; } NTSAliases[Count] = NULL; fclose(in); } } VOID SetupFwdAliases() { char ** Text = AliasText; char * Dest, * Alias; int Count = 0; char seps[] = " ,:/"; Aliases = zalloc(sizeof(struct ALIAS)); if (Text) { while(Text[0]) { Aliases = realloc(Aliases, (Count+2)* sizeof(struct ALIAS)); Aliases[Count] = zalloc(sizeof(struct ALIAS)); Dest = _strdup(Text[0]); // strtok changes it Dest = strtok_s(Dest, seps, &Alias); Aliases[Count]->Dest = Dest; Aliases[Count]->Alias = Alias; Count++; Text++; } Aliases[Count] = NULL; } } VOID SetupHAElements(struct BBSForwardingInfo * ForwardingInfo) { char * HText = ForwardingInfo->BBSHA; char * SaveHText, * ptr2; struct Continent * Continent; int Elements = 1; ForwardingInfo->BBSHAElements = zalloc(8); // always NULL entry on end even if no values SaveHText = _strdup(HText); ptr2 = SaveHText + strlen(SaveHText) -1; ForwardingInfo->BBSHAElements[0] = _strdup(WW); do { ForwardingInfo->BBSHAElements = realloc(ForwardingInfo->BBSHAElements, (Elements+2) * sizeof(void *)); while ((*ptr2 != '.') && (ptr2 > SaveHText)) { ptr2 --; } if (ptr2 == SaveHText) { // End ForwardingInfo->BBSHAElements[Elements++] = _strdup(ptr2); break; } ForwardingInfo->BBSHAElements[Elements++] = _strdup(ptr2+1); *ptr2 = 0; } while(TRUE); ForwardingInfo->BBSHAElements[Elements++] = NULL; if (ForwardingInfo->BBSHAElements[1]) { if (FOURCHARCONT == 0) { if (strlen(ForwardingInfo->BBSHAElements[1]) == 4) { // Convert to 2 char Continent; Continent = FindContinent(ForwardingInfo->BBSHAElements[1]); if (Continent) { free(ForwardingInfo->BBSHAElements[1]); ForwardingInfo->BBSHAElements[1] = _strdup(Continent->TwoCharCode); } } } else { if (strlen(ForwardingInfo->BBSHAElements[1]) == 2) { // Convert to 4 char Continent; Continent = FindContinent(ForwardingInfo->BBSHAElements[1]); if (Continent) { free(ForwardingInfo->BBSHAElements[1]); ForwardingInfo->BBSHAElements[1] = _strdup(Continent->FourCharCode); } } } } free(SaveHText); } VOID SetupHAddreses(struct BBSForwardingInfo * ForwardingInfo) { int Count=0; char ** HText = ForwardingInfo->Haddresses; char * SaveHText, * ptr2; // char * TopElement; char * Num; struct Continent * Continent; ForwardingInfo->HADDRS = zalloc(sizeof(void *)); // always NULL entry on end even if no values ForwardingInfo->HADDROffet = zalloc(sizeof(void *)); // always NULL entry on end even if no values while(HText[0]) { int Elements = 1; ForwardingInfo->HADDRS = realloc(ForwardingInfo->HADDRS, (Count+2) * sizeof(void *)); ForwardingInfo->HADDROffet = realloc(ForwardingInfo->HADDROffet, (Count+2) * sizeof(void *)); ForwardingInfo->HADDRS[Count] = zalloc(8); // Always at lesat WWW and NULL SaveHText = _strdup(HText[0]); Num = strlop(SaveHText, ','); ptr2 = SaveHText + strlen(SaveHText) -1; ForwardingInfo->HADDRS[Count][0] = _strdup(WW); if (strcmp(HText[0], "WW") != 0) { do { ForwardingInfo->HADDRS[Count] = realloc(ForwardingInfo->HADDRS[Count], (Elements+2) * sizeof(void *)); while ((*ptr2 != '.') && (ptr2 > SaveHText)) { ptr2 --; } if (ptr2 == SaveHText) { // End ForwardingInfo->HADDRS[Count][Elements++] = _strdup(ptr2); break; } ForwardingInfo->HADDRS[Count][Elements++] = _strdup(ptr2+1); *ptr2 = 0; } while(TRUE); } ForwardingInfo->HADDRS[Count][Elements++] = NULL; // If the route is not a complete HR (ie starting with a continent) // Add elemets from BBS HA to complete it. // (How do we know how many?? // ?? Still ont sure about this ?? // What i was trying to do was end up with a full HR, but knowing how many elements to use. // Far simpler to config it, but can users cope?? // Will config for testing. HA, N /* TopElement=ForwardingInfo->HADDRS[Count][0]; if (strcmp(TopElement, MyElements[0]) == 0) goto FullHR; if (FindContinent(TopElement)) goto FullHR; // Need to add stuff from our HR Elements--; if (Elements < MyElementCount) break; FullHR: */ ForwardingInfo->HADDROffet[Count] = (Num)? atoi(Num): 0; if (ForwardingInfo->HADDRS[Count][1]) { if (FOURCHARCONT == 0) { if (strlen(ForwardingInfo->HADDRS[Count][1]) == 4) { // Convert to 2 char Continent; Continent = FindContinent(ForwardingInfo->HADDRS[Count][1]); if (Continent) { free(ForwardingInfo->HADDRS[Count][1]); ForwardingInfo->HADDRS[Count][1] = _strdup(Continent->TwoCharCode); } } } else { if (strlen(ForwardingInfo->HADDRS[Count][1]) == 2) { // Convert to 4 char Continent; Continent = FindContinent(ForwardingInfo->HADDRS[Count][1]); if (Continent) { free(ForwardingInfo->HADDRS[Count][1]); ForwardingInfo->HADDRS[Count][1] = _strdup(Continent->FourCharCode); } } } } free(SaveHText); HText++; Count++; } ForwardingInfo->HADDRS[Count] = NULL; } VOID SetupHAddresesP(struct BBSForwardingInfo * ForwardingInfo) { int Count=0; char ** HText = ForwardingInfo->HaddressesP; char * SaveHText, * ptr2; // char * TopElement; char * Num; struct Continent * Continent; ForwardingInfo->HADDRSP = zalloc(sizeof(void *)); // always NULL entry on end even if no values while(HText[0]) { int Elements = 1; ForwardingInfo->HADDRSP = realloc(ForwardingInfo->HADDRSP, (Count+2) * sizeof(void *)); ForwardingInfo->HADDRSP[Count] = zalloc(2 * sizeof(void *)); // Always at lesat WWW and NULL SaveHText = _strdup(HText[0]); Num = strlop(SaveHText, ','); ptr2 = SaveHText + strlen(SaveHText) -1; ForwardingInfo->HADDRSP[Count][0] = _strdup(WW); if (strcmp(HText[0], "WW") != 0) { do { ForwardingInfo->HADDRSP[Count] = realloc(ForwardingInfo->HADDRSP[Count], (Elements+2) * sizeof(void *)); while ((*ptr2 != '.') && (ptr2 > SaveHText)) { ptr2 --; } if (ptr2 == SaveHText) { // End ForwardingInfo->HADDRSP[Count][Elements++] = _strdup(ptr2); break; } ForwardingInfo->HADDRSP[Count][Elements++] = _strdup(ptr2+1); *ptr2 = 0; } while(TRUE); } ForwardingInfo->HADDRSP[Count][Elements++] = NULL; if (ForwardingInfo->HADDRSP[Count][1]) { if (FOURCHARCONT == 0) { if (strlen(ForwardingInfo->HADDRSP[Count][1]) == 4) { // Convert to 2 char Continent; Continent = FindContinent(ForwardingInfo->HADDRSP[Count][1]); if (Continent) { free(ForwardingInfo->HADDRSP[Count][1]); ForwardingInfo->HADDRSP[Count][1] = _strdup(Continent->TwoCharCode); } } } else { if (strlen(ForwardingInfo->HADDRSP[Count][1]) == 2) { // Convert to 4 char Continent; Continent = FindContinent(ForwardingInfo->HADDRSP[Count][1]); if (Continent) { free(ForwardingInfo->HADDRSP[Count][1]); ForwardingInfo->HADDRSP[Count][1] = _strdup(Continent->FourCharCode); } } } } free(SaveHText); HText++; Count++; } ForwardingInfo->HADDRSP[Count] = NULL; } VOID CheckAndSend(struct MsgInfo * Msg, CIRCUIT * conn, struct UserInfo * bbs) { struct BBSForwardingInfo * ForwardingInfo = bbs->ForwardingInfo; if (ForwardToMe || _stricmp(bbs->Call, BBSName) != 0) // Don't forward to ourself - already here! (unless ForwardToMe set) { if ((conn == NULL) || (!(conn->BBSFlags & BBS) || (_stricmp(conn->UserPointer->Call, bbs->Call) != 0))) // Dont send back { set_fwd_bit(Msg->fbbs, bbs->BBSNumber); ForwardingInfo->MsgCount++; if (ForwardingInfo->SendNew) ForwardingInfo->FwdTimer = ForwardingInfo->FwdInterval - (2 + (rand() % 30)); //Short delay to prevent all starting at once } } else { // if User has Redirect to RMS set do it. struct UserInfo * user = LookupCall(Msg->to); if (user && user->flags & F_RMSREDIRECT) { user = FindBBS("RMS"); if (user) { CheckAndSend(Msg, conn, user); } Logprintf(LOG_BBS, conn, '?', "Message matches this BBS and RMS Redirect set - fwd to RMS"); } else Logprintf(LOG_BBS, conn, '?', "Message matches this BBS and ForwardToMe not set - not queuing message"); } } VOID UpdateB2Dest(struct MsgInfo * Msg, char * Alias) { int FileSize; char MsgFile[MAX_PATH]; FILE * hFile; char * MsgBytes; struct stat STAT; sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number); if (stat(MsgFile, &STAT) == -1) return; FileSize = STAT.st_size; hFile = fopen(MsgFile, "rb"); if (hFile == NULL) return; MsgBytes=malloc(FileSize + 100); // A bit of space for alias substitution on B2 fread(MsgBytes, 1, FileSize, hFile); fclose(hFile); if (MsgBytes) { char * To, * AT, * ATEND; To = strstr(MsgBytes, "To:"); if (To) { ATEND = strchr(To, '\r'); if (ATEND) { size_t i = ATEND - To; AT = memchr(To, '@', i); if (AT) { // Move Message up/down to make room for substitution size_t Diff = (ATEND - AT - 1) - strlen(Alias); size_t BeforeAT = AT - MsgBytes + 1; int j = 0; memmove(AT + 1, AT + Diff + 1, Msg->length - BeforeAT + 10); memcpy(AT + 1, Alias, strlen(Alias)); j = 1; Msg->length -= (int)Diff; // Write Back hFile = fopen(MsgFile, "wb"); if (hFile == NULL) return; fwrite(MsgBytes, 1, Msg->length, hFile); fclose(hFile); } } } } } int MatchMessagetoBBSList(struct MsgInfo * Msg, CIRCUIT * conn) { struct UserInfo * bbs; struct UserInfo * user; struct BBSForwardingInfo * ForwardingInfo; char ATBBS[41]=""; char RouteElements[41]; int Count = 0; char * HElements[20] = {NULL}; int Elements = 0; char * ptr2; int MyElement = 0; BOOL Flood = FALSE; char FullRoute[100]; struct Continent * Continent; struct Country * Country; struct ALIAS * Alias; struct UserInfo * RMS; int toLen; if (Msg->status == 'K') return 1; // No point, but don't want no route warning strcpy(RouteElements, Msg->via); Logprintf(LOG_BBS, conn, '?', "Msg %d Routing Trace To %s Via %s", Msg->number, Msg->to, RouteElements); // "NTS" Alias substitution is now done on P and T before any other processing // No, Not a good idea to use same aliss file for NTS and other messages // (What about OT 2*?) // If we need to alias other types, add a new file if (strchr(RouteElements, '!')) // Source Route ("Bang Routing") { char * bang = &RouteElements[strlen(RouteElements)]; while (*(--bang) != '!'); // Find last ! *(bang) = 0; // remove it; _strupr(++bang); strcpy(Msg->via, RouteElements); // Remove from via for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) { if (_stricmp(bbs->Call, bang) == 0) { Logprintf(LOG_BBS, conn, '?', "Routing Trace Source Route via %s", bbs->Call); CheckAndSend(Msg, conn, bbs); return 1; } } if (conn && !(conn->BBSFlags & BBS)) nodeprintf(conn, "Routing via %s requested but %s not known to this BBS\r", bang, bang); Logprintf(LOG_BBS, conn, '?', "Routing via %s requested but %s not known to this BBS", bang, bang); } if (Msg->type == 'T') { strlop(RouteElements, '.'); Alias = CheckForNTSAlias(Msg, RouteElements); if (Alias) { // Replace the AT in the message with the Alias Logprintf(LOG_BBS, conn, '?', "Routing Trace @%s taken from Alias File", Alias->Alias); strcpy(Msg->via, Alias->Alias); if (Msg->B2Flags & B2Msg) UpdateB2Dest(Msg, Alias->Alias); SaveMessageDatabase(); } strcpy(RouteElements, Msg->via); // May have changed } // See if AMPR.ORG Mail // Note message is probably already queued to SMTP or RMS // If for our domain leave here toLen = (int)strlen(Msg->via); if (_memicmp(&Msg->via[toLen - 8], "ampr.org", 8) == 0) { // message is for ampr.org char toCall[48]; char * via; strcpy(toCall, _strupr(Msg->via)); via = strlop(toCall, '@'); if (via == NULL) { // To and VIA already set up. This should only happen if message is for us Logprintf(LOG_BBS, conn, '?', "Routing Trace at %s is for us. Leave Here", AMPRDomain); return 1; } if (_stricmp(via, AMPRDomain) == 0) { // message is for us. Leave Here if (strlen(toCall) > 6) toCall[6] = 0; strcpy(Msg->to, toCall); strcpy(Msg->via, via); Logprintf(LOG_BBS, conn, '?', "Routing Trace at %s is for us. Leave Here", AMPRDomain); return 1; } if (SendAMPRDirect) { // We want to send ampr mail direct to host. Queue to BBS AMPR bbs = FindAMPR(); if (bbs) { // We have bbs AMPR if (_stricmp(Msg->to, "RMS") == 0 || Msg->to[0] == 0) { // Was set to go via RMS or ISP - change it strcpy(Msg->to, "AMPR"); } Logprintf(LOG_BBS, conn, '?', "Routing Trace to ampr.org Matches BBS AMPR"); set_fwd_bit(Msg->fbbs, bbs->BBSNumber); bbs->ForwardingInfo->MsgCount++; if (bbs->ForwardingInfo->SendNew) bbs->ForwardingInfo->FwdTimer = bbs->ForwardingInfo->FwdInterval; return 1; } // To AMPR, but don't have a BBS called AMPR - dropthrough in case we are forwarding AMPR to another BBS } } // See if sending @ winlink.org if (_stricmp(Msg->to, "RMS") == 0) { // If a user of this bbs with Poll RMS set, leave it here - no point in sending to winlink // To = RMS could come from RMS:EMAIL Address. If so, we only check user if @winlink.org, or // we will hold g8bpq@g8bpq.org.uk char * Call; char * AT; Call = _strupr(_strdup(Msg->via)); AT = strlop(Call, '@'); if (AT) { if (_stricmp(AT, "WINLINK.ORG") == 0) { user = LookupCall(Call); if (user) { if (user->flags & F_POLLRMS) { Logprintf(LOG_BBS, conn, '?', "Message @ winlink.org, but local RMS user - leave here"); strcpy(Msg->to, Call); strcpy(Msg->via, AT); free(Call); if (user->flags & F_BBS) // User is also a BBS, so set FWD bit so he can get it set_fwd_bit(Msg->fbbs, user->BBSNumber); return 1; } } } } free(Call); // To = RMS, but not winlink.org, or not local user. RMS = FindRMS(); if (RMS) { Logprintf(LOG_BBS, conn, '?', "Routing Trace to RMS Matches BBS RMS"); set_fwd_bit(Msg->fbbs, RMS->BBSNumber); RMS->ForwardingInfo->MsgCount++; if (RMS->ForwardingInfo->SendNew) RMS->ForwardingInfo->FwdTimer = RMS->ForwardingInfo->FwdInterval; return 1; } // To RMS, but don't have a BBS called RMS - dropthrough in case we are forwarding RMS to another BBS } if (_stricmp(RouteElements, "WINLINK.ORG") == 0) { user = LookupCall(Msg->to); if (user) { if (user->flags & F_POLLRMS) { Logprintf(LOG_BBS, conn, '?', "Routing Trace @ winlink.org, but local RMS user - leave here"); if (user->flags & F_BBS) // User is also a BBS, so set FWD bit so he can get it set_fwd_bit(Msg->fbbs, user->BBSNumber); return 1; // Route found } } RMS = FindRMS(); if (RMS) { Logprintf(LOG_BBS, conn, '?', "Routing Trace @ winlink.org Matches BBS RMS"); set_fwd_bit(Msg->fbbs, RMS->BBSNumber); RMS->ForwardingInfo->MsgCount++; if (RMS->ForwardingInfo->SendNew) RMS->ForwardingInfo->FwdTimer = RMS->ForwardingInfo->FwdInterval; return 1; } Logprintf(LOG_BBS, conn, '?', "Routing Trace - @ winlink.org but no BBS RMS"); return 0; } // See if a well-known alias Alias = FindAlias(RouteElements); if (Alias) { Logprintf(LOG_BBS, conn, '?', "Routing Trace Alias Substitution %s > %s", RouteElements, Alias->Alias); strcpy(RouteElements, Alias->Alias); if (conn) if ((ReaddressReceived && (conn->BBSFlags & BBS)) || (ReaddressLocal && ((conn->BBSFlags & BBS) == 0))) strcpy(Msg->via, Alias->Alias); } // Make sure HA is complete (starting at WW) if (RouteElements[0] == 0) { // Just a TO. If a Bull, set flood if (Msg->type == 'B') Flood = TRUE; goto NOHA; } ptr2 = RouteElements + strlen(RouteElements) - 1; while ((*ptr2 != '.') && (ptr2 > RouteElements)) { ptr2 --; } if (ptr2 != RouteElements) *ptr2++; if ((strcmp(ptr2, "WW") == 0) || (strcmp(ptr2, "WWW") == 0)) { strcpy(FullRoute, RouteElements); goto FULLHA; } if (FindContinent(ptr2)) { // Just need to add WW sprintf_s(FullRoute, sizeof(FullRoute),"%s.WW", RouteElements); goto FULLHA; } Country = FindCountry(ptr2); if (Country) { // Just need to add Continent and WW if (FOURCHARCONT == 0) sprintf_s(FullRoute, sizeof(FullRoute),"%s.%s.WW", RouteElements, Country->Continent2); else sprintf_s(FullRoute, sizeof(FullRoute),"%s.%s.WW", RouteElements, Country->Continent4); goto FULLHA; } // Don't know // Assume a local dis list, and set Flood. strcpy(FullRoute, RouteElements); if (Msg->type == 'B') Flood = TRUE; FULLHA: strcpy(ATBBS, FullRoute); strlop(ATBBS, '.'); if (FullRoute) { // Split it up ptr2 = FullRoute + strlen(FullRoute) - 1; do { while ((*ptr2 != '.') && (ptr2 > FullRoute)) { ptr2 --; } if (ptr2 != FullRoute) *ptr2++ = 0; HElements[Elements++] = ptr2; } while(Elements < 20 && ptr2 != FullRoute); // Just in case! } if (HElements[1]) { if (FOURCHARCONT == 0) { if (strlen(HElements[1]) == 4) { // Convert to 2 char Continent; Continent = FindContinent(HElements[1]); if (Continent) { // free(MyElements[1]); HElements[1] = _strdup(Continent->TwoCharCode); } } } else { if (strlen(HElements[1]) == 2) { // Convert to 4 char Continent; Continent = FindContinent(HElements[1]); if (Continent) { // free(MyElements[1]); HElements[1] = _strdup(Continent->FourCharCode); } } } } // if Bull, see if reached right area if (Msg->type == 'B') { int i = 0; // Log My HA for debugging Logprintf(LOG_BBS, conn, '?', "Routing Trace. Check if reached correct area My HA is %s", HRoute); // All elements of Helements must match Myelements while (MyElements[i] && HElements[i]) // Until one set runs out { if (strcmp(MyElements[i], HElements[i]) != 0) break; i++; } // if HElements[i+1] = NULL, have matched all if (HElements[i] == NULL) Flood = TRUE; } NOHA: Logprintf(LOG_BBS, conn, '?', "Routing Trace Type %c %sTO %s VIA %s Route On %s %s %s %s %s", Msg->type, (Flood) ? "(Flood) ":"", Msg->to, Msg->via, HElements[0], HElements[1], HElements[2], HElements[3], HElements[4]); if (Msg->type == 'T') { int depth = 0; int bestmatch = -1; struct UserInfo * bestbbs = NULL; // if NTS Traffic. Route on Wildcarded TO (best match). // If no match, route on AT (Should be NTSxx) // If no match, send to any BBS with routing to state XX and NTS flag set (no we dont!) for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) { ForwardingInfo = bbs->ForwardingInfo; depth = CheckBBSToForNTS(Msg, ForwardingInfo); if (depth > -1) { Logprintf(LOG_BBS, conn, '?', "Routing Trace NTS Matches TO BBS %s Length %d", bbs->Call, depth); if (depth > bestmatch) { bestmatch = depth; bestbbs = bbs; } } } if (bestbbs) { if (bestbbs->flags & F_NTSMPS) Logprintf(LOG_BBS, conn, '?', "Routing Trace NTS Best Match is %s, but NTS MPS Set so not queued", bestbbs->Call); else { Logprintf(LOG_BBS, conn, '?', "Routing Trace NTS Best Match is %s", bestbbs->Call); CheckAndSend(Msg, conn, bestbbs); } return 1; } // Check AT for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) { ForwardingInfo = bbs->ForwardingInfo; if (CheckBBSAtList(Msg, ForwardingInfo, ATBBS)) { if (bbs->flags & F_NTSMPS) Logprintf(LOG_BBS, conn, '?', "Routing Trace NTS %s Matches AT %s, but NTS MPS Set so not queued", ATBBS, bbs->Call); else { Logprintf(LOG_BBS, conn, '?', "Routing Trace NTS %s Matches AT %s", ATBBS, bbs->Call); CheckAndSend(Msg, conn, bbs); } return 1; } } goto CheckWildCardedAT; } // First check P message to multiple destinations // We should choose the BBS with most matching elements (ie match on #23.GBR better that GBR) // If SendPtoMultiple is set I think we send to any with same match level // So if SendPtoMultiple is set I think I need to find the best depth then send to all with the same depth // If none are found on HA match drop through. // But this means a message at the @BBS call will be forwarded without checking the Implied AT. So do that first if (SendPtoMultiple && Msg->type == 'P') { struct UserInfo * bestbbs = NULL; int bestmatch = 0; int depth; int Matched = 0; int MultiPDepth = 0; Count = 0; Logprintf(LOG_BBS, conn, '?', "SendPtoMultiple is set. Checking for best match level"); for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) { ForwardingInfo = bbs->ForwardingInfo; // Check Implied AT if ((strcmp(ATBBS, bbs->Call) == 0)) // @BBS = BBS { Logprintf(LOG_BBS, conn, '?', "Routing Trace %s Matches implied AT %s", ATBBS, bbs->Call); CheckAndSend(Msg, conn, bbs); return 1; } depth = CheckBBSHElements(Msg, bbs, ForwardingInfo, ATBBS, &HElements[0]); if (depth) { if (depth > MultiPDepth) { MultiPDepth = depth; bestbbs = bbs; } } } if (MultiPDepth) { for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) { ForwardingInfo = bbs->ForwardingInfo; depth = CheckBBSHElements(Msg, bbs, ForwardingInfo, ATBBS, &HElements[0]); if (depth == MultiPDepth) { Logprintf(LOG_BBS, conn, '?', "Routing Trace HR Matches BBS %s Depth %d", bbs->Call, depth); CheckAndSend(Msg, conn, bbs); Count++; } } return Count; } else Logprintf(LOG_BBS, conn, '?', "SendPtoMultiple is set but no match on HA"); } if (Msg->type == 'P' || Flood == 0) { // P messages are only sent to one BBS, but check the TO and AT of all BBSs before routing on HA, // and choose the best match on HA // now has option to send P messages to more than one BBS struct UserInfo * bestbbs = NULL; int bestmatch = 0; int depth; int Matched = 0; int MultiPDepth = 0; for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) { ForwardingInfo = bbs->ForwardingInfo; if (ForwardingInfo->PersonalOnly && (Msg->type != 'P') && (Msg->type != 'T')) continue; if (CheckBBSToList(Msg, bbs, ForwardingInfo)) { Logprintf(LOG_BBS, conn, '?', "Routing Trace TO %s Matches BBS %s", Msg->to, bbs->Call); CheckAndSend(Msg, conn, bbs); Matched++; if (SendPtoMultiple && Msg->type == 'P') continue; return 1; } } for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) { ForwardingInfo = bbs->ForwardingInfo; if (ForwardingInfo->PersonalOnly && (Msg->type != 'P')) continue; // Check implied AT if ((strcmp(ATBBS, bbs->Call) == 0)) // @BBS = BBS { Logprintf(LOG_BBS, conn, '?', "Routing Trace %s Matches implied AT %s", ATBBS, bbs->Call); CheckAndSend(Msg, conn, bbs); Matched++; if (SendPtoMultiple && Msg->type == 'P') continue; return 1; } // Check AT if (CheckBBSAtList(Msg, ForwardingInfo, ATBBS)) { Logprintf(LOG_BBS, conn, '?', "Routing Trace %s Matches AT %s", ATBBS, bbs->Call); CheckAndSend(Msg, conn, bbs); Matched++; if (SendPtoMultiple && Msg->type == 'P') continue; return 1; } } if (Matched) { // Should only get here if Sending P to Multiples is enabled // and we have at least one forward. return 1; } // Normal HA match (not SendPToMultiple) for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) { ForwardingInfo = bbs->ForwardingInfo; if (ForwardingInfo->PersonalOnly && (Msg->type != 'P')) continue; depth = CheckBBSHElements(Msg, bbs, ForwardingInfo, ATBBS, &HElements[0]); if (depth) { Logprintf(LOG_BBS, conn, '?', "Routing Trace HR Matches BBS %s Depth %d", bbs->Call, depth); if (depth > bestmatch) { bestmatch = depth; bestbbs = bbs; } } } if (bestbbs) { Logprintf(LOG_BBS, conn, '?', "Routing Trace HR Best Match is %s", bestbbs->Call); CheckAndSend(Msg, conn, bestbbs); return 1; } // Check for wildcarded AT address // if (ATBBS[0] == 0) // return FALSE; // no AT CheckWildCardedAT: depth = 0; bestmatch = -1; bestbbs = NULL; for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) { ForwardingInfo = bbs->ForwardingInfo; if (ForwardingInfo->PersonalOnly && (Msg->type != 'P')) continue; depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, ATBBS); if (depth > -1) { Logprintf(LOG_BBS, conn, '?', "Routing Trace Wildcarded AT Matches %s Length %d", bbs->Call, depth); if (depth > bestmatch) { bestmatch = depth; bestbbs = bbs; } } } if (bestbbs) { if (Msg->type == 'T' && (bestbbs->flags & F_NTSMPS)) Logprintf(LOG_BBS, conn, '?', "Routing Trace Wildcarded AT Best Match is %s, but NTS Msg and MPS Set so not queued", bestbbs->Call); else { Logprintf(LOG_BBS, conn, '?', "Routing Trace Wildcarded AT Best Match is %s", bestbbs->Call); CheckAndSend(Msg, conn, bestbbs); } return 1; } Logprintf(LOG_BBS, conn, '?', "Routing Trace - No Match"); return FALSE; // No match } // Flood Bulls go to all matching BBSs in the flood area, so the order of checking doesn't matter // For now I will only route on AT (for non-hierarchical addresses) and HA // Ver 1.0.4.2 - Try including TO for (bbs = BBSChain; bbs; bbs = bbs->BBSNext) { ForwardingInfo = bbs->ForwardingInfo; if (ForwardingInfo->PersonalOnly) continue; if (CheckBBSToList(Msg, bbs, ForwardingInfo)) { Logprintf(LOG_BBS, conn, '?', "Routing Trace TO %s Matches BBS %s", Msg->to, bbs->Call); if (ForwardToMe || _stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! (unless ForwardToMe set) { set_fwd_bit(Msg->fbbs, bbs->BBSNumber); ForwardingInfo->MsgCount++; } Count++; continue; } if ((strcmp(ATBBS, bbs->Call) == 0) || // @BBS = BBS CheckBBSAtList(Msg, ForwardingInfo, ATBBS)) { Logprintf(LOG_BBS, conn, '?', "Routing Trace AT %s Matches BBS %s", Msg->to, bbs->Call); CheckAndSend(Msg, conn, bbs); Count++; continue; } if (CheckBBSHElementsFlood(Msg, bbs, ForwardingInfo, Msg->via, &HElements[0])) { Logprintf(LOG_BBS, conn, '?', "Routing Trace HR %s %s %s %s %s Matches BBS %s", HElements[0], HElements[1], HElements[2], HElements[3], HElements[4], bbs->Call); CheckAndSend(Msg, conn, bbs); Count++; } } if (Count == 0) goto CheckWildCardedAT; Logprintf(LOG_BBS, conn, '?', "Routing Trace - No Match"); return Count; } BOOL CheckBBSToList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo) { char ** Calls; // Check TO distributions if (ForwardingInfo->TOCalls) { Calls = ForwardingInfo->TOCalls; while(Calls[0]) { if (strcmp(Calls[0], Msg->to) == 0) return TRUE; Calls++; } } return FALSE; } BOOL CheckBBSAtList(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS) { char ** Calls; // Check AT distributions // if (strcmp(ATBBS, bbs->Call) == 0) // @BBS = BBS // return TRUE; if (ForwardingInfo->ATCalls) { Calls = ForwardingInfo->ATCalls; while(Calls[0]) { if (strcmp(Calls[0], ATBBS) == 0) return TRUE; Calls++; } } return FALSE; } int CheckBBSHElements(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char ** HElements) { // Used for Personal Messages, and Bulls not yot at their target area char *** HRoutes; int i = 0, j, k = 0; int bestmatch = 0; if (ForwardingInfo->HADDRSP) { // Match on Routes HRoutes = ForwardingInfo->HADDRSP; k=0; while(HRoutes[k]) { i = j = 0; while (HRoutes[k][i] && HElements[j]) // Until one set runs out { if (strcmp(HRoutes[k][i], HElements[j]) != 0) break; i++; j++; } // Only send if all BBS elements match if (HRoutes[k][i] == 0) { if (i > bestmatch) bestmatch = i; } k++; } } return bestmatch; } int CheckBBSHElementsFlood(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char ** HElements) { char *** HRoutes; char ** BBSHA; int i = 0, j, k = 0; int bestmatch = 0; if (ForwardingInfo->HADDRS) { // Match on Routes // Message must be in right area (all elements of message match BBS Location HA) BBSHA = ForwardingInfo->BBSHAElements; if (BBSHA == NULL) return 0; // Not safe to flood i = j = 0; while (BBSHA[i] && HElements[j]) // Until one set runs out { if (strcmp(BBSHA[i], HElements[j]) != 0) break; i++; j++; } if (HElements[j] != 0) return 0; // Message is not for BBS's area HRoutes = ForwardingInfo->HADDRS; k=0; while(HRoutes[k]) { i = j = 0; while (HRoutes[k][i] && HElements[j]) // Until one set runs out { if (strcmp(HRoutes[k][i], HElements[j]) != 0) break; i++; j++; } if (i > bestmatch) { // As Flooding, only match if all elements match, and elements matching > offset // As Flooding, only match if all elements from BBS are matched // ie if BBS has #23.gbr.eu, and msg gbr.eu, don't match // if BBS has gbr.eu, and msg #23.gbr.eu, ok (so long as bbs in in #23, checked above. if (HRoutes[k][i] == 0) bestmatch = i; } k++; } } return bestmatch; } int CheckBBSToForNTS(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo) { char ** Calls; char * Call; char * ptr; int bestmatch = -1; int MatchLen = 0; // Look for Matches on TO using Wildcarded Addresses. Intended for use with NTS traffic, with TO = ZIPCode // We forward to the BBS with the most specific match - ie minimum *'s in match if (ForwardingInfo->TOCalls) { Calls = ForwardingInfo->TOCalls; while(Calls[0]) { BOOL Invert = FALSE; // !Match Call = Calls[0]; if (Call[0] == '!' || Call[0] == '-') { Call++; Invert = TRUE; } ptr = strchr(Call, '*'); if (ptr) { MatchLen = ptr - Call; if (memcmp(Msg->to, Call, MatchLen) == 0) { // Match - de we have a better one? // if it is a !Match, return without checking any more if (Invert) return -1; if (MatchLen > bestmatch) bestmatch = MatchLen; } } else { //no star - just do a normal compare if (strcmp(Msg->to, Call) == 0) { if (Invert) return -1; MatchLen = (int)strlen(Call); if (MatchLen > bestmatch) bestmatch = MatchLen; } } Calls++; } } return (int)bestmatch; } int CheckBBSATListWildCarded(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS) { char ** Calls; char * Call; char * ptr; int bestmatch = -1; // must be signed! int MatchLen = 0; // Look for Matches on AT using Wildcarded Addresses. Only applied after all other checks fail. Intended mainly // for setting a default route, but could have other uses // We forward to the BBS with the most specific match - ie minimum *'s in match if (ForwardingInfo->ATCalls) { Calls = ForwardingInfo->ATCalls; while(Calls[0]) { Call = Calls[0]; ptr = strchr(Call, '*'); // only look if * present - we have already tried routing on the full AT if (ptr) { MatchLen = ptr - Call; if (memcmp(ATBBS, Call, MatchLen) == 0) { // Match - de we have a better one? if (MatchLen > bestmatch) bestmatch = MatchLen; } } Calls++; } } return (int)bestmatch; } struct ALIAS * CheckForNTSAlias(struct MsgInfo * Msg, char * ATFirstElement) { struct ALIAS ** Alias = NTSAliases; char * Call; char * ptr; size_t MatchLen = 0; // We have a list of wildcarded TO (ie Zip Codes) and corresponding replacement NTSXX // It seems the NTS people want to match on either TO or DEST, and replace the AT // and to use only the first matching while(Alias[0]) { Call = Alias[0]->Dest; if (Call == NULL) break; ptr = strchr(Call, '*'); if (ptr) { MatchLen = ptr - Call; if (memcmp(Msg->to, Call, MatchLen) == 0) return(Alias[0]); } else { //no star - just do a normal compare if (strcmp(Msg->to, Call) == 0) return(Alias[0]); } // Try AT if (ATFirstElement && ATFirstElement[0]) { if (ptr) { if (memcmp(ATFirstElement, Call, MatchLen) == 0) return(Alias[0]); } else { if (strcmp(ATFirstElement, Call) == 0) return(Alias[0]); } } Alias++; } return NULL; } VOID ReRouteMessages() { // Pass all messages to the Mail Routing routine. // Used if a new BBS is set up, or to readdress messages from a failed BBS struct MsgInfo * Msg; int n; char SaveStatus; char Savefbbs[NBMASK]; for (n = 1; n <= NumberofMessages; n++) { Msg = MsgHddrPtr[n]; // if killed, or forwarded and not a bull, ignore // If status was F and we add anther BBS, set back to N if (Msg->status == 'K' || (Msg->status == 'F' && Msg->type != 'B')) continue; if (Msg->type == 'B') SaveStatus = Msg->status; memcpy(Savefbbs, Msg->fbbs, NBMASK); memset(Msg->fbbs, 0, NBMASK); MatchMessagetoBBSList(Msg, NULL); if (memcmp(Savefbbs, Msg->fbbs, NBMASK) != 0) { // Have changed Message Routing // Clear fwd bits on any BBS it has been sent to if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) { struct UserInfo * user; for (user = BBSChain; user; user = user->BBSNext) { if (check_fwd_bit(Msg->fbbs, user->BBSNumber)) // for this BBS? { if (check_fwd_bit(Msg->forw, user->BBSNumber)) // Already sent? clear_fwd_bit(Msg->fbbs, user->BBSNumber); } } // If still some bits set, change to $ if (memcmp(Msg->fbbs, zeros, NBMASK) != 0) { if (FirstMessageIndextoForward > n) FirstMessageIndextoForward = n; if (Msg->type == 'B') Msg->status = '$'; } else { // all clear - set to N or F if (Msg->type == 'B') { if (memcmp(Msg->forw, zeros, NBMASK) == 0) Msg->status = 'N'; // Not sent anywhere, and nowhere to send, so N else Msg->status = 'F'; } } } } } SaveMessageDatabase(); }