2022-08-28 09:35:46 +01:00
|
|
|
|
/*
|
|
|
|
|
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.
|
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// Use 4 char continent codes if this is set
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
int FOURCHARCONT = 0;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#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[] =
|
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
"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
|
2022-08-28 09:35:46 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct Country Countries[] =
|
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
"AFG", "ASIA", "AS", // Afghanistan
|
|
|
|
|
"ALA", "EURO", "EU", // <20>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
|
2024-12-16 17:54:16 +00:00
|
|
|
|
// "COM", "SAFR", "AF", // Comoros
|
2024-07-07 16:06:08 +01:00
|
|
|
|
"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<74>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
|
2022-08-28 09:35:46 +01:00
|
|
|
|
};
|
|
|
|
|
char ** AliasText;
|
|
|
|
|
struct ALIAS ** Aliases;
|
|
|
|
|
|
|
|
|
|
struct ALIAS ** NTSAliases = NULL;
|
|
|
|
|
|
|
|
|
|
/*struct ALIAS Aliases[] =
|
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
"AMSAT", "WW",
|
|
|
|
|
"USBBS", "USA",
|
|
|
|
|
"ALLUS", "USA"};
|
2022-08-28 09:35:46 +01:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
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];
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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];
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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];
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
Alias++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID SetupMyHA()
|
|
|
|
|
{
|
|
|
|
|
int Elements = 1; // Implied WW on front
|
|
|
|
|
char * ptr2;
|
|
|
|
|
struct Continent * Continent;
|
|
|
|
|
|
|
|
|
|
strcpy(MyRouteElements, HRoute);
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
// Split it up
|
|
|
|
|
|
|
|
|
|
ptr2 = MyRouteElements + strlen(MyRouteElements) - 1;
|
|
|
|
|
|
|
|
|
|
if (HRoute[0])
|
|
|
|
|
{
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
while ((*ptr2 != '.') && (ptr2 > MyRouteElements))
|
|
|
|
|
ptr2 --;
|
|
|
|
|
|
|
|
|
|
if (ptr2 == MyRouteElements)
|
|
|
|
|
{
|
|
|
|
|
// End
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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])
|
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
if (FOURCHARCONT == 0)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
if (strlen(MyElements[1]) == 4)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// Convert to 2 char Continent;
|
|
|
|
|
Continent = FindContinent(MyElements[1]);
|
|
|
|
|
if (Continent)
|
|
|
|
|
{
|
|
|
|
|
free(MyElements[1]);
|
|
|
|
|
MyElements[1] = _strdup(Continent->TwoCharCode);
|
|
|
|
|
}
|
2022-08-28 09:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-07 16:06:08 +01:00
|
|
|
|
else
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
if (strlen(MyElements[1]) == 2)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// Convert to 4 char Continent;
|
|
|
|
|
Continent = FindContinent(MyElements[1]);
|
|
|
|
|
if (Continent)
|
|
|
|
|
{
|
|
|
|
|
free(MyElements[1]);
|
|
|
|
|
MyElements[1] = _strdup(Continent->FourCharCode);
|
|
|
|
|
}
|
2022-08-28 09:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 *));
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
while ((*ptr2 != '.') && (ptr2 > SaveHText))
|
|
|
|
|
{
|
|
|
|
|
ptr2 --;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ptr2 == SaveHText)
|
|
|
|
|
{
|
|
|
|
|
// End
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
ForwardingInfo->BBSHAElements[Elements++] = _strdup(ptr2);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
ForwardingInfo->BBSHAElements[Elements++] = _strdup(ptr2+1);
|
|
|
|
|
*ptr2 = 0;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
} while(TRUE);
|
|
|
|
|
|
|
|
|
|
ForwardingInfo->BBSHAElements[Elements++] = NULL;
|
|
|
|
|
|
|
|
|
|
if (ForwardingInfo->BBSHAElements[1])
|
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
if (FOURCHARCONT == 0)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
if (strlen(ForwardingInfo->BBSHAElements[1]) == 4)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// Convert to 2 char Continent;
|
|
|
|
|
Continent = FindContinent(ForwardingInfo->BBSHAElements[1]);
|
|
|
|
|
if (Continent)
|
|
|
|
|
{
|
|
|
|
|
free(ForwardingInfo->BBSHAElements[1]);
|
|
|
|
|
ForwardingInfo->BBSHAElements[1] = _strdup(Continent->TwoCharCode);
|
|
|
|
|
}
|
2022-08-28 09:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-07 16:06:08 +01:00
|
|
|
|
else
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
if (strlen(ForwardingInfo->BBSHAElements[1]) == 2)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// Convert to 4 char Continent;
|
|
|
|
|
Continent = FindContinent(ForwardingInfo->BBSHAElements[1]);
|
|
|
|
|
if (Continent)
|
|
|
|
|
{
|
|
|
|
|
free(ForwardingInfo->BBSHAElements[1]);
|
|
|
|
|
ForwardingInfo->BBSHAElements[1] = _strdup(Continent->FourCharCode);
|
|
|
|
|
}
|
2022-08-28 09:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(SaveHText);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID SetupHAddreses(struct BBSForwardingInfo * ForwardingInfo)
|
|
|
|
|
{
|
|
|
|
|
int Count=0;
|
|
|
|
|
char ** HText = ForwardingInfo->Haddresses;
|
|
|
|
|
char * SaveHText, * ptr2;
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// char * TopElement;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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 *));
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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
|
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
/*
|
2022-08-28 09:35:46 +01:00
|
|
|
|
TopElement=ForwardingInfo->HADDRS[Count][0];
|
|
|
|
|
|
|
|
|
|
if (strcmp(TopElement, MyElements[0]) == 0)
|
2024-07-07 16:06:08 +01:00
|
|
|
|
goto FullHR;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
if (FindContinent(TopElement))
|
2024-07-07 16:06:08 +01:00
|
|
|
|
goto FullHR;
|
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
// Need to add stuff from our HR
|
|
|
|
|
|
|
|
|
|
Elements--;
|
|
|
|
|
|
|
|
|
|
if (Elements < MyElementCount)
|
2024-07-07 16:06:08 +01:00
|
|
|
|
break;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
FullHR:
|
|
|
|
|
*/
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
ForwardingInfo->HADDROffet[Count] = (Num)? atoi(Num): 0;
|
|
|
|
|
|
|
|
|
|
if (ForwardingInfo->HADDRS[Count][1])
|
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
if (FOURCHARCONT == 0)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
|
|
|
|
if (strlen(ForwardingInfo->HADDRS[Count][1]) == 4)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// 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);
|
|
|
|
|
}
|
2022-08-28 09:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-07 16:06:08 +01:00
|
|
|
|
else
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
if (strlen(ForwardingInfo->HADDRS[Count][1]) == 2)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// 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);
|
|
|
|
|
}
|
2022-08-28 09:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
free(SaveHText);
|
|
|
|
|
HText++;
|
|
|
|
|
Count++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ForwardingInfo->HADDRS[Count] = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID SetupHAddresesP(struct BBSForwardingInfo * ForwardingInfo)
|
|
|
|
|
{
|
|
|
|
|
int Count=0;
|
|
|
|
|
char ** HText = ForwardingInfo->HaddressesP;
|
|
|
|
|
char * SaveHText, * ptr2;
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// char * TopElement;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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 *));
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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])
|
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
if (FOURCHARCONT == 0)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
|
|
|
|
if (strlen(ForwardingInfo->HADDRSP[Count][1]) == 4)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// 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);
|
|
|
|
|
}
|
2022-08-28 09:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-07 16:06:08 +01:00
|
|
|
|
else
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
if (strlen(ForwardingInfo->HADDRSP[Count][1]) == 2)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// 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);
|
|
|
|
|
}
|
2022-08-28 09:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
free(SaveHText);
|
|
|
|
|
HText++;
|
|
|
|
|
Count++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ForwardingInfo->HADDRSP[Count] = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID CheckAndSend(struct MsgInfo * Msg, CIRCUIT * conn, struct UserInfo * bbs)
|
|
|
|
|
{
|
|
|
|
|
struct BBSForwardingInfo * ForwardingInfo = bbs->ForwardingInfo;
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2024-06-09 21:48:39 +01:00
|
|
|
|
if (ForwardToMe || _stricmp(bbs->Call, BBSName) != 0) // Don't forward to ourself - already here! (unless ForwardToMe set)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
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)
|
2024-06-09 21:48:39 +01:00
|
|
|
|
ForwardingInfo->FwdTimer = ForwardingInfo->FwdInterval - (2 + (rand() % 30)); //Short delay to prevent all starting at once
|
2022-08-28 09:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
}
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
Logprintf(LOG_BBS, conn, '?', "Message matches this BBS and RMS Redirect set - fwd to RMS");
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
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;
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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;
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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");
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// See if sending @ winlink.org
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
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
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
// 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);
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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");
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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");
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
RMS = FindRMS();
|
|
|
|
|
|
|
|
|
|
if (RMS)
|
|
|
|
|
{
|
|
|
|
|
Logprintf(LOG_BBS, conn, '?', "Routing Trace @ winlink.org Matches BBS RMS");
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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);
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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);
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
if (Country)
|
|
|
|
|
{
|
|
|
|
|
// Just need to add Continent and WW
|
2024-07-07 16:06:08 +01:00
|
|
|
|
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);
|
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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;
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
HElements[Elements++] = ptr2;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} while(Elements < 20 && ptr2 != FullRoute); // Just in case!
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (HElements[1])
|
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
if (FOURCHARCONT == 0)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
if (strlen(HElements[1]) == 4)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// Convert to 2 char Continent;
|
|
|
|
|
Continent = FindContinent(HElements[1]);
|
|
|
|
|
if (Continent)
|
|
|
|
|
{
|
|
|
|
|
// free(MyElements[1]);
|
|
|
|
|
HElements[1] = _strdup(Continent->TwoCharCode);
|
|
|
|
|
}
|
2022-08-28 09:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-07 16:06:08 +01:00
|
|
|
|
else
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
if (strlen(HElements[1]) == 2)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// Convert to 4 char Continent;
|
|
|
|
|
Continent = FindContinent(HElements[1]);
|
|
|
|
|
if (Continent)
|
|
|
|
|
{
|
|
|
|
|
// free(MyElements[1]);
|
|
|
|
|
HElements[1] = _strdup(Continent->FourCharCode);
|
|
|
|
|
}
|
2022-08-28 09:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// if Bull, see if reached right area
|
|
|
|
|
|
|
|
|
|
if (Msg->type == 'B')
|
|
|
|
|
{
|
|
|
|
|
int i = 0;
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2024-10-11 15:37:11 +01:00
|
|
|
|
// Log My HA for debugging
|
|
|
|
|
|
|
|
|
|
Logprintf(LOG_BBS, conn, '?', "Routing Trace. Check if reached correct area My HA is %s", HRoute);
|
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
// 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);
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// 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.
|
|
|
|
|
|
2024-10-11 15:37:11 +01:00
|
|
|
|
// But this means a message at the @BBS call will be forwarded without checking the Implied AT. So do that first
|
|
|
|
|
|
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
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;
|
|
|
|
|
|
2024-10-11 15:37:11 +01:00
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
depth = CheckBBSHElements(Msg, bbs, ForwardingInfo, ATBBS, &HElements[0]);
|
|
|
|
|
|
|
|
|
|
if (depth)
|
|
|
|
|
{
|
|
|
|
|
if (depth > MultiPDepth)
|
|
|
|
|
{
|
|
|
|
|
MultiPDepth = depth;
|
|
|
|
|
bestbbs = bbs;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
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");
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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;
|
2024-02-23 21:36:36 +00:00
|
|
|
|
int MultiPDepth = 0;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
for (bbs = BBSChain; bbs; bbs = bbs->BBSNext)
|
|
|
|
|
{
|
|
|
|
|
ForwardingInfo = bbs->ForwardingInfo;
|
|
|
|
|
|
|
|
|
|
if (ForwardingInfo->PersonalOnly && (Msg->type != 'P') && (Msg->type != 'T'))
|
|
|
|
|
continue;
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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);
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
CheckAndSend(Msg, conn, bbs);
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// Normal HA match (not SendPToMultiple)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
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);
|
2024-02-23 21:36:36 +00:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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);
|
2024-02-23 21:36:36 +00:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check for wildcarded AT address
|
|
|
|
|
|
2024-02-23 21:36:36 +00:00
|
|
|
|
// if (ATBBS[0] == 0)
|
|
|
|
|
// return FALSE; // no AT
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
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);
|
2024-02-23 21:36:36 +00:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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
|
2024-07-07 16:06:08 +01:00
|
|
|
|
}
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// Flood Bulls go to all matching BBSs in the flood area, so the order of checking doesn't matter
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// For now I will only route on AT (for non-hierarchical addresses) and HA
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// Ver 1.0.4.2 - Try including TO
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
for (bbs = BBSChain; bbs; bbs = bbs->BBSNext)
|
|
|
|
|
{
|
|
|
|
|
ForwardingInfo = bbs->ForwardingInfo;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
if (ForwardingInfo->PersonalOnly)
|
|
|
|
|
continue;
|
2024-02-23 21:36:36 +00:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
if (CheckBBSToList(Msg, bbs, ForwardingInfo))
|
|
|
|
|
{
|
|
|
|
|
Logprintf(LOG_BBS, conn, '?', "Routing Trace TO %s Matches BBS %s", Msg->to, bbs->Call);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
if (ForwardToMe || _stricmp(bbs->Call, BBSName) != 0) // Dont forward to ourself - already here! (unless ForwardToMe set)
|
2024-02-23 21:36:36 +00:00
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
set_fwd_bit(Msg->fbbs, bbs->BBSNumber);
|
|
|
|
|
ForwardingInfo->MsgCount++;
|
2024-02-23 21:36:36 +00:00
|
|
|
|
}
|
2024-07-07 16:06:08 +01:00
|
|
|
|
Count++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
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);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
Count++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
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);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
Count++;
|
2024-02-23 21:36:36 +00:00
|
|
|
|
}
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
}
|
2024-02-23 21:36:36 +00:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
if (Count == 0)
|
|
|
|
|
goto CheckWildCardedAT;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
Logprintf(LOG_BBS, conn, '?', "Routing Trace - No Match");
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
return Count;
|
|
|
|
|
}
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
BOOL CheckBBSToList(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo)
|
|
|
|
|
{
|
|
|
|
|
char ** Calls;
|
2024-02-23 21:36:36 +00:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// Check TO distributions
|
2024-02-23 21:36:36 +00:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
if (ForwardingInfo->TOCalls)
|
|
|
|
|
{
|
|
|
|
|
Calls = ForwardingInfo->TOCalls;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
while(Calls[0])
|
|
|
|
|
{
|
|
|
|
|
if (strcmp(Calls[0], Msg->to) == 0)
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
Calls++;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-07 16:06:08 +01:00
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
BOOL CheckBBSAtList(struct MsgInfo * Msg, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS)
|
|
|
|
|
{
|
|
|
|
|
char ** Calls;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// Check AT distributions
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// if (strcmp(ATBBS, bbs->Call) == 0) // @BBS = BBS
|
|
|
|
|
// return TRUE;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
if (ForwardingInfo->ATCalls)
|
|
|
|
|
{
|
|
|
|
|
Calls = ForwardingInfo->ATCalls;
|
2024-02-23 21:36:36 +00:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
while(Calls[0])
|
|
|
|
|
{
|
|
|
|
|
if (strcmp(Calls[0], ATBBS) == 0)
|
|
|
|
|
return TRUE;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
Calls++;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-07 16:06:08 +01:00
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
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)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// Match on Routes
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
HRoutes = ForwardingInfo->HADDRSP;
|
|
|
|
|
k=0;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
while(HRoutes[k])
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
i = j = 0;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
while (HRoutes[k][i] && HElements[j]) // Until one set runs out
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2024-07-07 16:06:08 +01:00
|
|
|
|
if (strcmp(HRoutes[k][i], HElements[j]) != 0)
|
|
|
|
|
break;
|
|
|
|
|
i++;
|
|
|
|
|
j++;
|
|
|
|
|
}
|
2024-02-23 21:36:36 +00:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// Only send if all BBS elements match
|
2024-02-23 21:36:36 +00:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
if (HRoutes[k][i] == 0)
|
|
|
|
|
{
|
|
|
|
|
if (i > bestmatch)
|
|
|
|
|
bestmatch = i;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
}
|
2024-07-07 16:06:08 +01:00
|
|
|
|
k++;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-07 16:06:08 +01:00
|
|
|
|
return bestmatch;
|
|
|
|
|
}
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
int CheckBBSHElementsFlood(struct MsgInfo * Msg, struct UserInfo * bbs, struct BBSForwardingInfo * ForwardingInfo, char * ATBBS, char ** HElements)
|
|
|
|
|
{
|
|
|
|
|
char *** HRoutes;
|
|
|
|
|
char ** BBSHA;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
int i = 0, j, k = 0;
|
|
|
|
|
int bestmatch = 0;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
if (ForwardingInfo->HADDRS)
|
|
|
|
|
{
|
|
|
|
|
// Match on Routes
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
// Message must be in right area (all elements of message match BBS Location HA)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
BBSHA = ForwardingInfo->BBSHAElements;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
if (BBSHA == NULL)
|
|
|
|
|
return 0; // Not safe to flood
|
2024-02-23 21:36:36 +00:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
i = j = 0;
|
2024-02-23 21:36:36 +00:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
while (BBSHA[i] && HElements[j]) // Until one set runs out
|
|
|
|
|
{
|
|
|
|
|
if (strcmp(BBSHA[i], HElements[j]) != 0)
|
|
|
|
|
break;
|
|
|
|
|
i++;
|
|
|
|
|
j++;
|
|
|
|
|
}
|
2024-02-23 21:36:36 +00:00
|
|
|
|
|
2024-07-07 16:06:08 +01:00
|
|
|
|
if (HElements[j] != 0)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
return 0; // Message is not for BBS's area
|
|
|
|
|
|
|
|
|
|
HRoutes = ForwardingInfo->HADDRS;
|
|
|
|
|
k=0;
|
|
|
|
|
|
|
|
|
|
while(HRoutes[k])
|
|
|
|
|
{
|
|
|
|
|
i = j = 0;
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
while (HRoutes[k][i] && HElements[j]) // Until one set runs out
|
|
|
|
|
{
|
|
|
|
|
if (strcmp(HRoutes[k][i], HElements[j]) != 0)
|
|
|
|
|
break;
|
|
|
|
|
i++;
|
|
|
|
|
j++;
|
|
|
|
|
}
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
if (i > bestmatch)
|
|
|
|
|
{
|
|
|
|
|
// As Flooding, only match if all elements match, and elements matching > offset
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
// 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
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
// 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
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
// 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
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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, '*');
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
if (ptr)
|
|
|
|
|
{
|
|
|
|
|
MatchLen = ptr - Call;
|
|
|
|
|
|
|
|
|
|
if (memcmp(Msg->to, Call, MatchLen) == 0)
|
|
|
|
|
return(Alias[0]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//no star - just do a normal compare
|
2024-07-07 16:06:08 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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?
|
2024-07-07 16:06:08 +01:00
|
|
|
|
clear_fwd_bit(Msg->fbbs, user->BBSNumber);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|