├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── question.md └── workflows │ ├── cmake-ubuntu.yml │ └── codeql.yml ├── CMakeLists.txt ├── CREDITS.txt ├── LICENSE ├── README.md ├── README.original.txt ├── README_FSFDT.txt ├── SECURITY.md ├── program_directory ├── unix │ ├── adminhelp.txt │ ├── cert.txt │ ├── fsd.conf │ ├── fsd.sh │ ├── fsd_d.sh │ ├── killfsd.sh │ ├── motd.txt │ └── rebootfsd.sh └── windows │ ├── Readme.txt │ ├── Rebooot Service.bat │ ├── Run.bat │ ├── Service Install.bat │ ├── Service Uninstall.bat │ ├── cert.txt │ ├── fsd.conf │ └── motd.txt └── src ├── attributes.h ├── authenticate.c ├── authenticate.cpp ├── authenticate.h ├── certificate.cpp ├── certificate.h ├── client.cpp ├── client.h ├── clinterface.cpp ├── clinterface.h ├── cluser.cpp ├── cluser.h ├── config.cpp ├── config.h ├── fsd.cpp ├── fsd.h ├── fsdpaths.h ├── global.h ├── interface.cpp ├── interface.h ├── main.cpp ├── manage.cpp ├── manage.h ├── mm.cpp ├── mm.h ├── process.cpp ├── process.h ├── protocol.h ├── server.cpp ├── server.h ├── servinterface.cpp ├── servinterface.h ├── servuser.cpp ├── servuser.h ├── support.cpp ├── support.h ├── sysinterface.cpp ├── sysinterface.h ├── sysuser.cpp ├── sysuser.h ├── user.cpp ├── user.h ├── wprofile.cpp └── wprofile.h /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Bug Report \U0001F41B" 3 | about: 创建 Bug 报告以帮助我们改进 / Create a report to help us improve 4 | title: "\U0001F41B[BUG] " 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | 18 | 19 | ### 🐛 描述(Description) 20 | 24 | 25 | 26 | ### 📷 复现步骤(Steps to Reproduce) 27 | 28 | 34 | 1. 35 | 2. 36 | 3. 37 | 38 | ### 📄 日志信息(Log Information) 39 | 47 | 48 | ### 🚑 基本信息(Basic Information) 49 | 50 | - 程序版本(Program Version): 51 | - 系统版本号(OS Version): 52 | 53 | ### 🖼 截图(Screenshots) 54 | 55 | 59 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Feature request \U0001F451" 3 | about: 对程序的需求或建议 / Suggest an idea for program 4 | title: "\U0001F451[Enhancement] " 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | 16 | 17 | ### 🥰 需求描述(Description) 18 | 19 | 23 | 24 | ### 🧐 解决方案(Solution) 25 | 26 | 30 | 31 | ### 🚑 其他信息(Other Information) 32 | 33 | 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Question \U0001F9D0" 3 | about: 对程序使用的疑问或需要帮助 / Questions about the use of the program or need help 4 | title: "\U0001F9D0[Question] " 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | 16 | 17 | ### 🧐 问题描述(Description) 18 | 19 | 23 | 24 | ### 🚑 其他信息(Other Information) 25 | 26 | 30 | 31 | - 程序版本(Program Version): 32 | - 系统版本号(OS Version): 33 | -------------------------------------------------------------------------------- /.github/workflows/cmake-ubuntu.yml: -------------------------------------------------------------------------------- 1 | name: cmake-ubuntu 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 11 | BUILD_TYPE: Release 12 | 13 | jobs: 14 | build: 15 | # The CMake configure and build commands are platform agnostic and should work equally 16 | # well on Windows or Mac. You can convert this to a matrix build if you need 17 | # cross-platform coverage. 18 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | 24 | - name: Configure CMake 25 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 26 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 27 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 28 | 29 | - name: Build 30 | # Build your program with the given configuration 31 | run: | 32 | cd ${{github.workspace}}/build 33 | make 34 | 35 | - name: MV 36 | run: | 37 | cd ${{github.workspace}}/build 38 | mv fsd ${{github.workspace}}/program_directory/unix/ 39 | 40 | - name: 'Upload Artifact' 41 | uses: actions/upload-artifact@v2 42 | with: 43 | name: Betterfsd_linux_amd64 44 | path: ${{github.workspace}}/program_directory/unix/ 45 | retention-days: 5 46 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "master" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ "master" ] 20 | schedule: 21 | - cron: '36 6 * * 0' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'cpp' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v3 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v2 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | 52 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 53 | # queries: security-extended,security-and-quality 54 | 55 | 56 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). 57 | # If this step fails, then you should remove it and run the build manually (see below) 58 | - name: Autobuild 59 | uses: github/codeql-action/autobuild@v2 60 | 61 | # ℹ️ Command-line programs to run using the OS shell. 62 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 63 | 64 | # If the Autobuild fails above, remove it and uncomment the following three lines. 65 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 66 | 67 | # - run: | 68 | # echo "Run, Build Application using script" 69 | # ./location_of_script_within_repo/buildscript.sh 70 | 71 | - name: Perform CodeQL Analysis 72 | uses: github/codeql-action/analyze@v2 73 | with: 74 | category: "/language:${{matrix.language}}" 75 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | project(fsd 3 | VERSION 3.99.1 4 | LANGUAGES CXX) 5 | 6 | add_executable(fsd 7 | src/authenticate.cpp 8 | src/certificate.cpp 9 | src/client.cpp 10 | src/clinterface.cpp 11 | src/cluser.cpp 12 | src/config.cpp 13 | src/fsd.cpp 14 | src/interface.cpp 15 | src/main.cpp 16 | src/manage.cpp 17 | src/mm.cpp 18 | src/process.cpp 19 | src/server.cpp 20 | src/servinterface.cpp 21 | src/servuser.cpp 22 | src/support.cpp 23 | src/sysinterface.cpp 24 | src/sysuser.cpp 25 | src/user.cpp 26 | src/wprofile.cpp) 27 | 28 | if (UNIX) 29 | find_package(SQLite3 REQUIRED) 30 | endif (UNIX) 31 | if (MSVC) 32 | set_property(TARGET fsd PROPERTY VS_PACKAGE_REFERENCES "System.Data.SQLite_1.0.115.5") 33 | endif (MSVC) 34 | target_link_libraries(fsd sqlite3) 35 | 36 | set_property(TARGET fsd PROPERTY CMAKE_CXX_STANDARD 17) 37 | set_property(TARGET fsd PROPERTY CMAKE_CXX_STANDARD_REQUIRED ON) 38 | -------------------------------------------------------------------------------- /CREDITS.txt: -------------------------------------------------------------------------------- 1 | Thanks to all the people who have helped me getting this server to what it is 2 | now. I couldn't have done it without these people: 3 | 4 | Greg Ament 5 | John Eisenhour 6 | Jason Grooms 7 | Joe Jurecka 8 | David Kings 9 | Mladen Kmetic 10 | Lyndon Nerenberg 11 | 12 | And ofcourse: 13 | 14 | All server operators 15 | All people who sent me bug reports 16 | All members of the SB/PC beta mailing list 17 | All members of the FSFAN team Dordrecht 18 | 19 | 20 | Marty Bochane 21 | 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BetterFSD 2 | [![Ubuntu](https://github.com/nlohmann/json/workflows/Ubuntu/badge.svg)](https://github.com/LinkTechTips/BetterFSD/actions?query=workflow%3AUbuntu) 3 | [![CMake](https://github.com/LinkTechTips/BetterFSD/actions/workflows/cmake-ubuntu.yml/badge.svg)](https://github.com/LinkTechTips/BetterFSD/actions/workflows/cmake-ubuntu.yml) 4 | [![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/nlohmann/json.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/LinkTechTips/BetterFSD/context:cpp) 5 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/LinkTechTips/BetterFSD/blob/master/LICENSE) 6 | [![GitHub Issues](https://img.shields.io/github/issues/LinkTechTips/json.svg)](https://github.com/LinkTechTips/BetterFSD/issues) 7 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FLinkTechTips%2FBetterFSD.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2FLinkTechTips%2FBetterFSD?ref=badge_shield) 8 | 9 | ## 关于 10 | 本项目是在"Marty Bochane's FSD 2"基础上修改而来的 11 | 不保证源码能否正常工作 12 | 有问题欢迎提交issue或PR 13 | 14 | ## 君子协议 15 | 16 | 本项目基于Apache 2.0协议开源 17 | 若要使用本项目 请给本项目一个star 18 | 19 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FLinkTechTips%2FBetterFSD.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2FLinkTechTips%2FBetterFSD?ref=badge_large) 20 | 21 | ## 已实现功能 22 | * 航向输出 23 | * 秒级whazzup更新 24 | * 更稳定的FSD 25 | * 报文显示 26 | 27 | ## 待实现功能 28 | * 使用mySQL作为cert数据库 29 | * Echo防崩溃 30 | -------------------------------------------------------------------------------- /README.original.txt: -------------------------------------------------------------------------------- 1 | FSD v2.0 Release notes. 2 | ======================= 3 | 4 | This is the 2.0 release of FSD, the multiserver system for Squawkbox/Pro 5 | Controller clients. 6 | 7 | In this package, you'll find these directories: 8 | 9 | fsd : Here's the source of the FSD system. 10 | libdbms : This is the MDBMS library. 11 | etc : This is the directory that contains sample configuration and help 12 | files. 13 | docs : Here are some docs. Mmm, here will be some docs. 14 | 15 | Here are the steps you need to take to get to a running system: 16 | 17 | Step 1 : Compiling 18 | ================== 19 | Configuration of the server code is handled by the GNU autoconf 20 | system. Type './configure' to configure the servercode. The preferred 21 | directories are these: 22 | 23 | Component Directory 24 | ---------------------------------------------------------- 25 | fsd server binary /usr/local/sbin/ 26 | fsd help files /usr/local/etc/fsd/ 27 | 28 | The configure script will ask you where the binary and the help files 29 | should be placed. If you are not root, make sure the path you specify 30 | here is accessible. 31 | After running the configure script, type 'make' and then 'make install'. 32 | 33 | 34 | Sorry, no man pages yet. 35 | 36 | Step 2 : Configuring the server 37 | =============================== 38 | All the files you need to run the server are in the 'fsd help files' directory. 39 | Here you'll find the following files: 40 | fsd.conf.sample : The configuration file. 41 | help.txt : The online help file for the system interface. 42 | motd.txt.sample : The message that will be shown to clients once they connect. 43 | 44 | All configuration takes place in the fsd.conf file. You must edit this file 45 | before you start the server. First copy the fsd.conf.sample file to fsd.conf, 46 | and then edit it. There's not much that needs to be changed. 47 | Here's what you need to do: 48 | 49 | - Find the 'system' group, it looks like [system] 50 | - Edit the 'ident' field. This is the identification of the server. It needs 51 | to be unique in the network. This ident will later be provided, but for the 52 | purpose of beta testing, just make up a unique ident. Make sure it doesn't 53 | contain spaces or 'strange' characters. You can use something like: 54 | ident=marty, or ident=serv1. Just make sure it's a unique name. 55 | NOTE: Please don't make this field too long, it has to be sent in every 56 | packet. Usually, 5 characters should be enough 57 | - Edit the email field. This is your email address. 58 | - Edit the hostname field. This is the hostname or the IP address of your 59 | server. 60 | - Edit the name field. This is a textual description of your server. 61 | It may contains spaces. Examples: 'Test server 1' or 'METAR weather server'. 62 | It's not really important what you put here. 63 | - Edit the password field. This field contains the password you will have 64 | to enter if you want to access privileged commands on the system port. 65 | Leave the field empty if you don't want to use the password protection. 66 | - Edit the location field. This is the physical location of the server in 67 | the world. You could enter a state or a city here. It may contain spaces. 68 | - Save the file. 69 | 70 | You may want to edit the motd.txt file, to send a personal greeting to 71 | your clients. 72 | 73 | If you want to test with more than 1 server on the same host, make sure 74 | that the clientport/serverport/systemport in the config files all have 75 | unique values. 76 | 77 | Now you're ready for your first testrun. 78 | 79 | Step 3 : Start the server 80 | ========================= 81 | Now we'll start the server for the first time. Make sure you are in the 82 | target directory and you have the 'fsd' binary. 83 | Now type ./fsd& to start the server. 84 | If you've done that, you should find a file 'log.txt' in the 'help files' 85 | directory. Look at this file to see if there are any errors here. 86 | You should see the line 'We are up' at the bottom. 87 | 88 | Step 4 : Test the server 89 | ======================== 90 | 91 | Upon startup, the server opens port 3012 (or whatever you configured). 92 | This port can be used to manage the server. You can simply use telnet 93 | to connect to this port: 94 | type 'telnet localhost 3012' 95 | 96 | You should now see a prompt. 97 | 98 | From here you can issue commands. There's online help available with the 99 | 'help' command. 100 | 101 | If you configured a password, you must enter this password before you can use 102 | any privileged commands. You can do that by typing: 'pwd ' 103 | Try that first. If you see 'password correct', you have access to 104 | privileged commands. 105 | 106 | The first thing you should check is the timezone. Type 'time' to see the 107 | local and UTC time. If these are not correct you should update the clock 108 | and/or the timezone of your machine. 109 | 110 | Another useful tool is the 'log' command. This command can be used to 111 | quickly extract entries from the logfile. For example: if you want to 112 | quickly check if anything went wrong, you type 'log show 3' (Show me 113 | all messages that are at least of level WARNING). You may want 114 | to type 'log delete 3' to delete these messages. Note that the deletion of 115 | messages does not effect the log file itself. To learn more about the 116 | log command, type 'help log'. 117 | 118 | If you have access to Squawkbox or Pro Controller software, you should 119 | check if it can connect to your server. 120 | 121 | 122 | Step 5 : Connecting to the network (optional) 123 | ============================================= 124 | If your server will be on the internet 24 hours a day, you can connect 125 | to the FSD network. Don't do this unless you have enough bandwith available, 126 | and you want to accept clients to test. If you have enough bandwith, go ahead, 127 | you can always decide to disconnect from the network later. 128 | 129 | Connectin to the network is very simple. You'll only have to find 130 | the host address of another server running FSD. There's a list of servers 131 | available at http://www.hinttech.com/~marty/sbserver. Once you are connected 132 | to the network, your address will appear here automatically. Don't bother 133 | trying my server (at hinttech), it's behind a firewall and I only use it 134 | for METAR and databases features. You won't be able to reach it. 135 | 136 | Make sure that you choose a server with a fast link and a minimal amount of 137 | hops to your server. When we start testing, there might not be many servers in 138 | the network, so your choice is limited, but there could be more servers every 139 | day, so it might be wise to check regularly to see if there's a better server 140 | nearby. If you find more than one server with a good link, you can decide 141 | to connect to both. You can test the connectivity to a server by using the 142 | standard unix tools 'ping' and traceroute. Once you've found a good server, 143 | connect to the system port of your local servers and type 'connect '. 144 | If all goes well, you should see 'Connection established'. 145 | 146 | After a while a complete server list should be available with the command 147 | 'servers'. If anything fails you can view the log file, or use the 'log' 148 | command to find more information. 149 | 150 | Congratulations, you are now part of the (experimental) fsd network. 151 | All network configurations, like the certificates and the weather system 152 | will be available automatically. 153 | 154 | We will now check the weather system. 155 | Type 'weather KJFK'. If for some reason you get the message 'No METAR host 156 | in network' then there is something wrong in the network, and you won't 157 | be able to resolve weather requests. If there is a METAR server in the 158 | network, the server will respond with the message 'weather request sent 159 | to METAR host '. You should now receive an overview of the current 160 | weather at KJFK (New York). 161 | 162 | Step 6 : Learn more about the system 163 | ==================================== 164 | 165 | I put some small hints here to get you on your way. All functionality 166 | of the server has to be documented in the final release (any documenters out 167 | there ?) 168 | 169 | You can use the online help to find more information about the available 170 | commands. Here's some more background information: 171 | 172 | You can always use the 'servers' command to get an overview of the servers. 173 | The 'Lag' field should be monitored now and then. It indicates the amount 174 | of seconds it takes for a packet to make a trip to this server. This Lag value 175 | should not get too high. If all the values are high, this indicates that 176 | your connection to the other server is bad and you might consider connecting 177 | to a different server. You can use the 'disconnect' command to disconnect a 178 | server connection. If you have clients online, make sure that at least one 179 | server connection remains. 180 | 181 | The 'ping' command can be used to check the connection to a single server. 182 | 183 | The 'connect' command without parameters shows important information about 184 | the current server connections. Especially the Feed value is interesting. 185 | It indicates the average amount of bytes per seconds that went through 186 | the link in the last 10 minutes. The In-Q and Out-Q should not get too 187 | big, otherwise you're probably dealing with a slow link. If this happens 188 | regularly, you should consider connecting to a different server. 189 | 190 | Certificates can be viewed with the 'cert' command. You can also add, delete 191 | and modify certificates with the this command. Be careful with this, your 192 | changes will be available in the entire network! 193 | 194 | You might wonder about the security of the server network. The current beta 195 | servers do not have much security features. You can control who is allowed 196 | to connect to your serverport by changing the 'allowfrom' line in the config 197 | file. You'll have to restart the server to make this work. 198 | 199 | The server has an autoconnect feature which makes sure that server connections 200 | will be restored when one is disconnected. After a disconnect on the server 201 | port, the server will attempt to reconnect after 2 minutes. This feature 202 | is only enabled when you made the first connect, and not for any connection 203 | to your server that might have been initiated by other servers. The server 204 | will repeat the connection attempts every 2 minutes forever if it fails. 205 | To stop this process, use the 'delguard' command. 206 | 207 | The 'clients' command can be used to view the list of clients that are 208 | in the network. If you specify a name as the argument to clients, you'll 209 | get an overview of this client. A pattern can also be specified. For 210 | example 'clients rt' will show all clients that have 'rt' in their callsigns, 211 | like 'marty' or 'bert' 212 | -------------------------------------------------------------------------------- /README_FSFDT.txt: -------------------------------------------------------------------------------- 1 | FSD for Unix and Windows 2 | ------------------------ 3 | 4 | This distrib is made to make a single source for FSD on Unix and Windows 5 | 6 | to compile fsd for unix : 7 | move to fsd subfolder : cd fsd 8 | make the project : make 9 | the fsd will be generated in unix directory 10 | clean the project : make clean 11 | 12 | to compile fsd for windows : 13 | launch Visual Studio 6 (or above) with fsd/fsd.dsw 14 | the fsd Release will be generated in windows directory 15 | clean the project removing generated Debug or Release folder 16 | 17 | 18 | For Windows : 19 | There is scripts to run interactivly FSD, install and run FSD as a service, uninstall the service 20 | 21 | For Unix : 22 | Look fsd.sh to install fsd as autostart at server boot 23 | Launch ./fsd_d.sh to launch fsd as daemon process 24 | Launch killfsd.sh to kill the daemon 25 | 26 | 27 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | 6 | | Version | Supported | 7 | | ------- | ------------------ | 8 | | 0.1-alpha | :white_check_mark: | 9 | | 0.2-alpha | :white_check_mark: | 10 | | 0.21-alpha | :white_check_mark: | 11 | 12 | ## Reporting a Vulnerability 13 | 14 | Email:3174327625@qq.com 15 | -------------------------------------------------------------------------------- /program_directory/unix/adminhelp.txt: -------------------------------------------------------------------------------- 1 | %topic global:MAIN 2 | FSD server version 1.0 3 | 4 | Try 'help ' to get help on a topic. A list of topics is available 5 | with the 'help topics' command. 6 | 7 | Commands available: 8 | 9 | %topic connect:CONNECT TO A SERVER 10 | Syntax: 11 | connect [ []] 12 | 13 | Will connect the local server to the specified server. Without arguments, 14 | the command will show the current connections. For every connection 15 | some information is shown: 16 | fd : The file descriptor that can be used for "disconnect" 17 | Out-Q : The amount of bytes waiting to be sent on this connection. 18 | In-Q : The amount of bytes waiting to be processed from this connection. 19 | Feed : The throughput of this connection is bytes/second (last 10 min.). 20 | Peer : The peer of this connection. 21 | 22 | %topic disconnect:DISCONNECT FROM A SERVER 23 | Syntax: 24 | disconnect 25 | 26 | Will disconnect the indicated server connection. Use 'connect' to get a list 27 | of active server connections. 28 | 29 | %topic servers:SERVER OVERVIEW(1) 30 | Syntax: 31 | servers [] 32 | 33 | This command will show an overview of the servers in the network. 34 | When the optional , is specified, only servers with in their 35 | ident code or in their name will be shown. 36 | 37 | The following fields are shown: 38 | ID : The server ID. 39 | Host : The hostname of the server. 40 | Email : The maintainer's email address. 41 | Fl : The flags associated with the server. M = Metar capable server, 42 | S = silent server, not for clients. 43 | Hops : The amount of hops needed to reach the host. 44 | Lag : The amount of seconds currently needed for a round trip. 45 | Name : The description of a server. 46 | 47 | 48 | %topic servers2:SERVER OVERVIEW(2) 49 | Syntax: 50 | servers2 [] 51 | 52 | This command will show an overview of the servers in the network. 53 | When the optional , is specified, only servers with in their 54 | ident code or in their name will be shown. 55 | 56 | The following fields are shown: 57 | ID : The server ID. 58 | Version : The server version. 59 | Email : The maintainer's email address. 60 | Location: The location of the server 61 | 62 | 63 | %topic ping:PING A NETWORK SERVER 64 | Syntax: 65 | ping 66 | 67 | This command sends a PING request to the specified server. When this servers 68 | receives the ping, it will send it back to the originating host. 69 | When a reply has been received, the reply time will be shown. 70 | 71 | %topic route:SHOW ROUTING TABLES 72 | Syntax 73 | route [] 74 | 75 | Will show the internal routing table to all servers, or only to the servers 76 | which have the in their ident. 77 | 78 | The routing table is configured automatically and does not need human 79 | intervention. 80 | 81 | %topic say:SEND A MESSAGE TO A CLIENT 82 | Syntax 83 | say 84 | 85 | This command sends a message to a client. 86 | can be either: 87 | - A broadcast address (* = all clients, *P = all pilots, *A = all ATC) 88 | - A callsign, like "KL348" 89 | - A frequency address, like "@11240" 90 | 91 | %topic clients:CLIENTS OVERVIEW 92 | Syntax 93 | clients [] 94 | 95 | This command will show a list of all clients matching the given callsign. 96 | If only one client matches the callsign, a detailed overview of this 97 | client is shown. 98 | 99 | %topic distance:CALCULATE DISTANCE 100 | Syntax 101 | distance 102 | 103 | This command can be used to calculate the distance between two clients. 104 | It only works if both client have reported their positions. The distance 105 | will be shown in Nautical Miles. 106 | 107 | %topic cert:CERTIFICATE CONTROL 108 | Syntax: 109 | cert 110 | cert add 111 | cert delete 112 | cert modify 113 | 114 | This command will add/delete or modify the certificate in the network. 115 | can either be 'P' for a Pilot certificate or 'A' for an ATC certificate. 116 | The maximum level of this certificate is . 117 | can be one of: 118 | 119 | Observer Instructor1 120 | Student1 Instructor2 121 | Student2 Instructor3 122 | Student3 Supervisor 123 | Controller1 Administrator 124 | Controller2 125 | Controller3 126 | 127 | %topic cert:SHOW CERTIFICATES 128 | Syntax: 129 | cert [] 130 | 131 | This command will show an overview of the current certificates. If 132 | is specified, only certificates which have in their code will be 133 | shown. 134 | 135 | %topic time:SHOW CURRENT TIME 136 | Syntax: 137 | time 138 | 139 | This command gives the current time. The local time will be shown, as well 140 | as the UTC time. 141 | 142 | %topic range:SHOW COM RANGE 143 | Syntax: 144 | range 145 | 146 | This command shows the communication range of the client with 147 | in NM. 148 | 149 | %topic weather:SEND WEATHER REQUEST 150 | Syntax: 151 | weather 152 | 153 | This command will send out a weather request to the nearest METAR capable 154 | server. If no such server exists in the network, the command will fail. 155 | is the name of the requested weather profile. Usually 156 | this is an ICAO station code like KJFK, but it can also be a hand made 157 | profile. 158 | 159 | Note that because the server has to send out a request, the prompt will 160 | be returned immediately. When a response has been received, it will be 161 | displayed. 162 | 163 | %topic metar:SEND METAR REQUEST 164 | Syntax: 165 | metar 166 | 167 | This command will send out a weather request to the nearest METAR capable 168 | server. If no such server exists in the network, the command will fail. 169 | is the name of the requested weather profile. Usually 170 | this is an ICAO station code like KJFK, but it can also be a hand made 171 | profile. 172 | 173 | Note that because the server has to send out a request, the prompt will 174 | be returned immediately. When a response has been received, it will be 175 | displayed. 176 | 177 | 178 | %topic log:ACCESS SYSTEM LOGS 179 | Syntax: 180 | log (show|delete) [] 181 | 182 | This command will show or delete log messages. If is specified, 183 | only messages will be shown/deleted. 184 | 185 | The importance of the log messages can be specified with the 186 | option. can be one if: 187 | 188 | 0 Critical messages 3 Warning messages 189 | 1 Alert messages 4 Info messages 190 | 2 Error messages 5 Debug messages 191 | 192 | 193 | %topic stat:SHOW SERVER STATISTICS 194 | Syntax: 195 | stat [] 196 | 197 | This command will show the server statistical entries. When is 198 | specified, only the entries with in their name will be shown. 199 | 200 | %topic wall:BROADCAST MESSAGE 201 | Syntax: 202 | wall 203 | 204 | This command will send the text to all clients connected to the 205 | local server. 206 | 207 | 208 | %topic delguard:DELETE GUARD CONNECTIONS 209 | Syntax: 210 | delguard 211 | 212 | This command will delete all guard connections (pending connections). 213 | 214 | 215 | %topic wp:CONTROL WEATHER PROFILES 216 | Syntax: 217 | wp show [] 218 | wp create 219 | wp delete 220 | wp activate 221 | wp set = 222 | 223 | Examples: 224 | wp create eham 225 | wp set eham visibility 6 226 | wp set eham pressure 2110 227 | wp set eham clouds.1.floor 5000 228 | wp set eham clouds.1.ceiling 9000 229 | wp set eham clouds.1.coverage 6 230 | wp set eham winds.1.floor 0 231 | wp set eham winds.1.ceiling 250 232 | wp set eham winds.1.speed 11 233 | wp set eham winds.1.direction 240 234 | wp set eham temperature.1.temperature 12 235 | wp activate eham 236 | 237 | %topic pos:SET LOCAL POSITION 238 | Syntax: 239 | pos 240 | 241 | This command is used to set the local position for the weather system. 242 | The command 'weather' uses this position to calculate the upper winds. 243 | 244 | %topic refreshmetar:DOWNLOAD LATEST METAR 245 | Syntax: 246 | refreshmetar 247 | 248 | This command downloads the latest weather reports and places these in the 249 | file 'metar.txt'. This command is not available if the metar source was set 250 | to 'network'. 251 | -------------------------------------------------------------------------------- /program_directory/unix/cert.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkTechTips/BetterFSD/7a058e79e40ec211f51394a8bf5f189f06edf017/program_directory/unix/cert.txt -------------------------------------------------------------------------------- /program_directory/unix/fsd.conf: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Sample configuration file for FSD 3 | # 4 | 5 | ############################################################################### 6 | # The system group holds information about your server. 7 | # 8 | # clientport & serverport: 9 | # The ports where clients and servers will connect to 10 | # systemport: 11 | # The port where the system management services will be located 12 | # ident: 13 | # The ident of your server. This ident has to be unique. It is used to 14 | # identify your server in the global network. It should not contain spaces. 15 | # Please use a small ident code, it will be sent in every packet 16 | # email: 17 | # The email address that can be used to mail the maintainer of this server. 18 | # name: 19 | # The name(description) of your server. It may contain spaces. 20 | # hostname: 21 | # The hostname that can be used to reach this server. 22 | # password: 23 | # The password you need to specify before you can execute privileged 24 | # commands on the system port. 25 | # location: 26 | # The (physical) location of the server in the world, and the internet. 27 | # Example: 'Delft, The netherlands (SURFnet)' 28 | # mode: 29 | # The mode of the server; can be 'normal' or 'silent'. Use 'normal' 30 | # for normal operation. 31 | # certificates: 32 | # The file to read certificates from. 33 | # maxclients: 34 | # The maximum amount of clients this server will allow 35 | # whazzup: 36 | # The file to put WhazzUp data in. 37 | 38 | [system] 39 | clientport=6809 40 | serverport=3011 41 | systemport=3010 42 | ident=FSD 43 | email=nobody@nowhere.com 44 | name=FSFDT FSD Unix Windows server 45 | hostname=localhost 46 | password=disable 47 | location=Nowhere 48 | mode=normal 49 | certificates=cert.txt 50 | maxclients=200 51 | whazzup=whazzup.txt 52 | whazzupjson=whazzup.json 53 | 54 | ############################################################################### 55 | # The connections group holds information about the (server) connections this 56 | # server wil establish and accept. 57 | # 58 | # connectto: 59 | # Contains the hostname and port numbers of the servers to connect to. 60 | # Multiple servers can be used here. For example: 61 | # connectto=server.hinttech.com:5001,server.flightsim.com:4006 62 | # allowfrom: 63 | # Contains the IP addresses from which servers can connect to this server. 64 | # Multiple IP addresses can be used, separated by commas. For example: 65 | # allowfrom=server.flightsim.com,atc.aol.com 66 | 67 | [connections] 68 | #connectto= 69 | #allowfrom= 70 | 71 | ############################################################################### 72 | # The hosts group contains a list of hosts that are trusted for some activity. 73 | # There are 2 entries: 74 | # certificates : contains a list of server ID's that are allowed to change 75 | # certificates 76 | # weather : contains a list of server ID's that are allowed to change 77 | # weather profiles 78 | #[hosts] 79 | #certificates= 80 | #weather= 81 | 82 | ############################################################################### 83 | # This group controls the weather system. 84 | # The 'source' variable determines the source of the METAR data. 85 | # For normal operation, set this to 'network'. 86 | # There are 3 possible values here: 87 | # 'file' : Read the METAR data from the file 'metar.txt' 88 | # and allow weather requests from other servers. 89 | # 'download' : Like 'file', but refresh metar.txt every hour by downloading 90 | # the latest weather observations from metlab. The server has 91 | # to be connected to the internet for this to work. 92 | # 'network' : Relay weather requests to the closest METAR capable server. 93 | # 94 | # 'server','dir' and 'ftpmode' are only used when the METAR source is 'download'. These 95 | # fields determine the host name and the directory from where metar data is 96 | # read. FSD uses the FTP protocol to get the data. ftpmode can have the value 'active' 97 | # 'passive' that are Active and Passive FTP protocol mode, default is 'passive'. 98 | # If you use FSD on a computer having a private IP, only use passive mode. 99 | 100 | [weather] 101 | source=download 102 | server=tgftp.nws.noaa.gov 103 | dir=data/observations/metar/cycles/ 104 | ftpmode=passive 105 | -------------------------------------------------------------------------------- /program_directory/unix/fsd.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | #----- This file is intend to be used as automatic start and stop of FSD 3 | #----- at server boot and server shutdown 4 | #----- Copy this file to /etc/init.d as root : cp fsd /etc/init.d 5 | #----- Link as startup the init.d file to /etc/rc3.d : ln -s /etc/init.d/fsd /etc/rc3.d/S85fsd 6 | #----- Link as shutdown the init.d file to /etc/rc3.d : ln -s /etc/init.d/fsd /etc/rc3.d/K15fsd 7 | # START OF CONFIG SECTION 8 | # WARNING ! For security reasons we advise: DO NOT RUN THE SERVER AS ROOT 9 | #----- USER is the unix user that will run fsd process, you can set user root 10 | #----- but it is not recommanded. Create a unix user to run fsd 11 | USER=fsd 12 | #----- DIR must point to fsd installation path 13 | DIR=/home/fsd/fsd 14 | # END OF CONFIG SECTION 15 | 16 | # See how we were called. 17 | case "$1" in 18 | start) 19 | WD=`pwd` 20 | cd $DIR 21 | su $USER -c "nohup ./fsd &" & 22 | cd $WD 23 | ;; 24 | stop) 25 | killall -9 fsd 26 | ;; 27 | restart) 28 | $0 stop && $0 start || exit 1 29 | ;; 30 | *) 31 | echo "Usage: $0 {start|stop|restart}" 32 | exit 2 33 | esac 34 | exit 0 35 | -------------------------------------------------------------------------------- /program_directory/unix/fsd_d.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | nohup ./fsd & 4 | -------------------------------------------------------------------------------- /program_directory/unix/killfsd.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | killall -9 fsd 4 | -------------------------------------------------------------------------------- /program_directory/unix/motd.txt: -------------------------------------------------------------------------------- 1 | Welcome to the FSD server for Unix. 2 | This Unix version has a united source code with the Windows version 3 | This Unix version is now able to generate whazzup file for servinfo 4 | 5 | Enjoy 6 | The FSFDT Team 7 | -------------------------------------------------------------------------------- /program_directory/unix/rebootfsd.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | killall -9 fsd 4 | nohup ./fsd & 5 | -------------------------------------------------------------------------------- /program_directory/windows/Readme.txt: -------------------------------------------------------------------------------- 1 | This is a beta version of FSFDT Windows FSD server 2 | 3 | 4 | USAGE : FSD 5 | /INSTALL : Install as Autostart Windows service 6 | /UNINSTALL : Uninstall service 7 | /RUN : Run Interactive 8 | 9 | 10 | 11 | 12 | Once installed as service, you can control its start/stop 13 | thru Administration tools/Services. 14 | 15 | 16 | FSD looks and writes file in the directory where fsd.exe is, 17 | so never separate directory between fsd.exe and config files. -------------------------------------------------------------------------------- /program_directory/windows/Rebooot Service.bat: -------------------------------------------------------------------------------- 1 | fsd /UNINSTALL 2 | fsd /Install 3 | pause 4 | -------------------------------------------------------------------------------- /program_directory/windows/Run.bat: -------------------------------------------------------------------------------- 1 | fsd /RUN 2 | pause 3 | -------------------------------------------------------------------------------- /program_directory/windows/Service Install.bat: -------------------------------------------------------------------------------- 1 | fsd /Install 2 | pause 3 | -------------------------------------------------------------------------------- /program_directory/windows/Service Uninstall.bat: -------------------------------------------------------------------------------- 1 | fsd /UNINSTALL 2 | pause 3 | -------------------------------------------------------------------------------- /program_directory/windows/cert.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinkTechTips/BetterFSD/7a058e79e40ec211f51394a8bf5f189f06edf017/program_directory/windows/cert.txt -------------------------------------------------------------------------------- /program_directory/windows/fsd.conf: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Sample configuration file for FSD 3 | # Attention : WINDOWS-format : required CR in this file! 4 | # 5 | 6 | ############################################################################### 7 | # The system group holds information about your server. 8 | # 9 | # clientport & serverport: 10 | # The ports where clients and servers will connect to 11 | # systemport: 12 | # The port where the system management services will be located 13 | # ident: 14 | # The ident of your server. This ident has to be unique. It is used to 15 | # identify your server in the global network. It should not contain spaces. 16 | # Please use a small ident code, it will be sent in every packet 17 | # email: 18 | # The email address that can be used to mail the maintainer of this server. 19 | # name: 20 | # The name(description) of your server. It may contain spaces. 21 | # hostname: 22 | # The hostname that can be used to reach this server. 23 | # password: 24 | # The password you need to specify before you can execute privileged 25 | # commands on the system port. 26 | # location: 27 | # The (physical) location of the server in the world, and the internet. 28 | # Example: 'Delft, The netherlands (SURFnet)' 29 | # mode: 30 | # The mode of the server; can be 'normal' or 'silent'. Use 'normal' 31 | # for normal operation. 32 | # certificates: 33 | # The file to read certificates from. 34 | # maxclients: 35 | # The maximum amount of clients this server will allow 36 | # whazzup: 37 | # The file to put WhazzUp data in. 38 | 39 | [system] 40 | clientport=6809 41 | serverport=3011 42 | systemport=3010 43 | ident=FSD 44 | email=nobody@nowhere.com 45 | name=FSFDT FSD Windows server 46 | hostname=localhost 47 | password=disable 48 | location=Nowhere 49 | mode=normal 50 | certificates=cert.txt 51 | maxclients=200 52 | whazzup=whazzup.txt 53 | whazzupjson=whazzup.json 54 | 55 | ############################################################################### 56 | # The connections group holds information about the (server) connections this 57 | # server wil establish and accept. 58 | # 59 | # connectto: 60 | # Contains the hostname and port numbers of the servers to connect to. 61 | # Multiple servers can be used here. For example: 62 | # connectto=server.hinttech.com:5001,server.flightsim.com:4006 63 | # allowfrom: 64 | # Contains the IP addresses from which servers can connect to this server. 65 | # Multiple IP addresses can be used, separated by commas. For example: 66 | # allowfrom=server.flightsim.com,atc.aol.com 67 | 68 | [connections] 69 | #connectto= 70 | #allowfrom= 71 | 72 | ############################################################################### 73 | # The hosts group contains a list of hosts that are trusted for some activity. 74 | # There are 2 entries: 75 | # certificates : contains a list of server ID's that are allowed to change 76 | # certificates 77 | # weather : contains a list of server ID's that are allowed to change 78 | # weather profiles 79 | #[hosts] 80 | #certificates= 81 | #weather= 82 | 83 | ############################################################################### 84 | # This group controls the weather system. 85 | # The 'source' variable determines the source of the METAR data. 86 | # For normal operation, set this to 'network'. 87 | # There are 3 possible values here: 88 | # 'file' : Read the METAR data from the file 'metar.txt' 89 | # and allow weather requests from other servers. 90 | # 'download' : Like 'file', but refresh metar.txt every hour by downloading 91 | # the latest weather observations from metlab. The server has 92 | # to be connected to the internet for this to work. 93 | # 'network' : Relay weather requests to the closest METAR capable server. 94 | # 95 | # 'server','dir' and 'ftpmode' are only used when the METAR source is 'download'. These 96 | # fields determine the host name and the directory from where metar data is 97 | # read. FSD uses the FTP protocol to get the data. ftpmode can have the value 'active' 98 | # 'passive' that are Active and Passive FTP protocol mode, default is 'passive'. 99 | # If you use FSD on a computer having a private IP, only use passive mode. 100 | 101 | [weather] 102 | source=download 103 | server=tgftp.nws.noaa.gov 104 | dir=data/observations/metar/cycles/ 105 | ftpmode=passive 106 | -------------------------------------------------------------------------------- /program_directory/windows/motd.txt: -------------------------------------------------------------------------------- 1 | Welcome to the first native FSD server for Windows. 2 | All config files and logs are in Windows text format. 3 | This fsd server can run as NT service, meaning it can 4 | run on a PC even if no user is logged. 5 | Additionally, network efficiency has been greatly 6 | improved and can support up to 1024 users. 7 | In futur, this FSD will include a embedded webserver to 8 | provide native ServInfo service, a new weather engine, 9 | and reporting in ODBC. 10 | 11 | 12 | Enjoy 13 | The FSFDT Team 14 | -------------------------------------------------------------------------------- /src/attributes.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MDBMS source code 3 | * 4 | * (c) 1997 HINT InterNetworking Technologies 5 | * 6 | * This is beta software. This version of mdbms can be used without 7 | * charge. Redistribution in source and binary forms, without any 8 | * modification, is permitted. 9 | * 10 | * DISCLAIMER: THIS SOFTWARE IS PROVIDED 'AS IS'. NO RESPONSIBILITY FOR 11 | * ANY DAMAGE CAUSED BY THE USE OF THIS SOFTWARE, CAN BE ACCEPTED BY 12 | * THE AUTHOR AND/OR PUBLISHER. 13 | * 14 | * See the README.source file for information. 15 | * 16 | * Please report bugs/fixes to marty@hinttech.com 17 | */ 18 | 19 | #ifndef attributesh 20 | #define attributesh 21 | extern char *attnames[]; 22 | #define ATT_INT 1 23 | #define ATT_FLOAT 2 24 | #define ATT_MONEY 3 25 | #define ATT_CHAR 4 26 | #define ATT_VARCHAR 5 27 | #define ATT_DATE 6 28 | #define ATT_BLOB 7 29 | #define ATT_NULL 10 30 | 31 | #define TBL_NORMAL 1 32 | #define TBL_VIEW 2 33 | #define TBL_SYSTEM 3 34 | 35 | #define DB_SYSTEM 1 36 | #define DB_NORMAL 2 37 | #endif 38 | -------------------------------------------------------------------------------- /src/authenticate.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Authentication routines for validating network connections. 3 | */ 4 | 5 | #ifdef __GNUC__ 6 | # ident "$Id: authenticate.c,v 2.01 1998/11/19 10:18:12 marty Exp $" 7 | #endif 8 | 9 | #ifdef WIN32 10 | #include 11 | #include 12 | #include 13 | #include "authenticate.h" 14 | #else 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "authenticate.h" 24 | #endif 25 | /* 26 | * Number of times to retry hostname lookup after temporary DNS failures. 27 | */ 28 | #define NUM_RETRY 2 29 | 30 | /* 31 | * Domain suffix in which we look up IP addresses. 32 | */ 33 | #define RBL_DOMAIN ".mcdu.com" 34 | 35 | /* FUNCTION: auth_validate_ip 36 | * 37 | * Validate an IP address against the banned addresses list. 38 | * 39 | * Returns 1 if the address is allowed to connect, or 0 if 40 | * the address is banned. 41 | */ 42 | int 43 | auth_validate_ip(struct sockaddr_in *sa) 44 | { 45 | char *ips; /* IPV4 address in dotted quad notation */ 46 | char *fqdn; /* FQDN to look up */ 47 | struct hostent *he; /* Result from gethostbyname */ 48 | int count; /* Retry counter for hostname lookups */ 49 | 50 | ips = inet_ntoa(sa->sin_addr); 51 | if (ips == NULL) { 52 | /* XXX log something */ 53 | return 1; 54 | } 55 | fqdn = malloc(strlen(ips) + sizeof(RBL_DOMAIN)); 56 | if (fqdn == NULL) { 57 | /* XXX log something */ 58 | return 1; 59 | } 60 | strcpy(fqdn, ips); 61 | strcat(fqdn, RBL_DOMAIN); 62 | 63 | for (count = 0; count < NUM_RETRY; count++) { 64 | 65 | he = gethostbyname(fqdn); 66 | if (he == NULL) { 67 | 68 | #ifdef WIN32 69 | switch (WSAGetLastError()) 70 | { 71 | case WSAHOST_NOT_FOUND: /* No entry - host is okay */ 72 | free(fqdn); 73 | return 1; 74 | break; 75 | 76 | case WSATRY_AGAIN: /* Temporary DNS error. Sleep and retry. */ 77 | Sleep(2); 78 | continue; 79 | break; 80 | 81 | /* 82 | * These errors indicate DNS problems. Default to letting 83 | * the client in. 84 | */ 85 | case WSANO_DATA: /* Exists in DNS, but no A record? */ 86 | case WSANO_RECOVERY: /* Permanent DNS server error. */ 87 | default: /* Other unknown error condition */ 88 | free(fqdn); 89 | return 1; 90 | break; 91 | 92 | } 93 | #else 94 | switch (h_errno) { 95 | 96 | case HOST_NOT_FOUND: /* No entry - host is okay */ 97 | free(fqdn); 98 | return 1; 99 | break; 100 | 101 | case TRY_AGAIN: /* Temporary DNS error. Sleep and retry. */ 102 | sleep(2); 103 | continue; 104 | break; 105 | 106 | /* 107 | * These errors indicate DNS problems. Default to letting 108 | * the client in. 109 | */ 110 | case NO_DATA: /* Exists in DNS, but no A record? */ 111 | case NO_RECOVERY: /* Permanent DNS server error. */ 112 | default: /* Other unknown error condition */ 113 | free(fqdn); 114 | return 1; 115 | break; 116 | 117 | } 118 | #endif 119 | } 120 | } 121 | 122 | free(fqdn); 123 | 124 | if (he == NULL) { 125 | /* Couldn't find a host entry */ 126 | return 1; 127 | } else { 128 | /* Found a valid A record. Punt the client. */ 129 | return 0; 130 | } 131 | /*NOTREACHED*/ 132 | } 133 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /src/authenticate.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Authentication routines for validating network connections. 3 | */ 4 | 5 | #ifdef WIN32 6 | #include 7 | #include 8 | #include 9 | #include "authenticate.h" 10 | #else 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include "authenticate.h" 20 | #endif 21 | /* 22 | * Number of times to retry hostname lookup after temporary DNS failures. 23 | */ 24 | #define NUM_RETRY 2 25 | 26 | /* 27 | * Domain suffix in which we look up IP addresses. 28 | */ 29 | #define RBL_DOMAIN ".mcdu.com" 30 | 31 | /* FUNCTION: auth_validate_ip 32 | * 33 | * Validate an IP address against the banned addresses list. 34 | * 35 | * Returns 1 if the address is allowed to connect, or 0 if 36 | * the address is banned. 37 | */ 38 | int 39 | auth_validate_ip(struct sockaddr_in *sa) 40 | { 41 | char *ips; /* IPV4 address in dotted quad notation */ 42 | char *fqdn; /* FQDN to look up */ 43 | struct hostent *he; /* Result from gethostbyname */ 44 | int count; /* Retry counter for hostname lookups */ 45 | 46 | ips = inet_ntoa(sa->sin_addr); 47 | if (ips == NULL) { 48 | /* XXX log something */ 49 | return 1; 50 | } 51 | fqdn = (char*)malloc(strlen(ips) + sizeof(RBL_DOMAIN)); 52 | if (fqdn == NULL) { 53 | /* XXX log something */ 54 | return 1; 55 | } 56 | strcpy(fqdn, ips); 57 | strcat(fqdn, RBL_DOMAIN); 58 | 59 | for (count = 0; count < NUM_RETRY; count++) { 60 | 61 | he = gethostbyname(fqdn); 62 | if (he == NULL) { 63 | 64 | #ifdef WIN32 65 | switch (WSAGetLastError()) 66 | { 67 | case WSAHOST_NOT_FOUND: /* No entry - host is okay */ 68 | free(fqdn); 69 | return 1; 70 | break; 71 | 72 | case WSATRY_AGAIN: /* Temporary DNS error. Sleep and retry. */ 73 | Sleep(2); 74 | continue; 75 | break; 76 | 77 | /* 78 | * These errors indicate DNS problems. Default to letting 79 | * the client in. 80 | */ 81 | case WSANO_DATA: /* Exists in DNS, but no A record? */ 82 | case WSANO_RECOVERY: /* Permanent DNS server error. */ 83 | default: /* Other unknown error condition */ 84 | free(fqdn); 85 | return 1; 86 | break; 87 | 88 | } 89 | #else 90 | switch (h_errno) { 91 | 92 | case HOST_NOT_FOUND: /* No entry - host is okay */ 93 | free(fqdn); 94 | return 1; 95 | break; 96 | 97 | case TRY_AGAIN: /* Temporary DNS error. Sleep and retry. */ 98 | sleep(2); 99 | continue; 100 | break; 101 | 102 | /* 103 | * These errors indicate DNS problems. Default to letting 104 | * the client in. 105 | */ 106 | case NO_DATA: /* Exists in DNS, but no A record? */ 107 | case NO_RECOVERY: /* Permanent DNS server error. */ 108 | default: /* Other unknown error condition */ 109 | free(fqdn); 110 | return 1; 111 | break; 112 | 113 | } 114 | #endif 115 | } 116 | } 117 | 118 | free(fqdn); 119 | 120 | if (he == NULL) { 121 | /* Couldn't find a host entry */ 122 | return 1; 123 | } else { 124 | /* Found a valid A record. Punt the client. */ 125 | return 0; 126 | } 127 | /*NOTREACHED*/ 128 | } 129 | -------------------------------------------------------------------------------- /src/authenticate.h: -------------------------------------------------------------------------------- 1 | #ifdef __GNUC__ 2 | # ident "$Id: authenticate.h,v 2.01 1998/11/19 10:18:12 marty Exp $" 3 | #endif 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif /* __cplusplus */ 8 | 9 | int auth_validate_ip (struct sockaddr_in *sa); 10 | 11 | #ifdef __cplusplus 12 | } 13 | #endif /* __cplusplus */ 14 | -------------------------------------------------------------------------------- /src/certificate.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "certificate.h" 6 | #include "global.h" 7 | 8 | const char *certlevels[]= 9 | { 10 | "SUSPENDED", "OBSPILOT", "STUDENT1", "STUDENT2", "STUDENT3", 11 | "CONTROLLER1", "CONTROLLER2", "CONTROLLER3", "INSTRUCTOR1", 12 | "INSTRUCTOR2", "INSTRUCTOR3", "SUPERVISOR", "ADMINISTRATOR" 13 | }; 14 | certificate *rootcert=NULL; 15 | certificate::certificate(char *c, char *p, int l, time_t crea, char *o) 16 | { 17 | next=rootcert, prev=NULL; 18 | if (next) next->prev=this; 19 | rootcert=this, livecheck=1; 20 | cid=strdup(c), password=strdup(p), level=l, origin=strdup(o); 21 | if (level>LEV_MAX) level=LEV_MAX; 22 | prevvisit=(time_t) 0, creation=crea; 23 | } 24 | certificate::~certificate() 25 | { 26 | if (next) next->prev=prev; 27 | if (prev) prev->next=next; else rootcert=next; 28 | free(cid); free(password); free(origin); 29 | } 30 | void certificate::configure(char *pwd, int l, time_t c, char *o) 31 | { 32 | level=l; 33 | if (pwd) 34 | { 35 | if (password) free(password); 36 | password=strdup(pwd); 37 | } 38 | if (o) 39 | { 40 | free(origin); 41 | origin=strdup(o); 42 | } 43 | creation=c; 44 | } 45 | int maxlevel(char *id, char *p, int *max) 46 | { 47 | certificate *temp=getcert(id); 48 | if (!temp) 49 | { 50 | *max=LEV_OBSPILOT; 51 | return 0; 52 | } 53 | if (!STRCASECMP(temp->password,p)) 54 | { 55 | *max=temp->level; 56 | temp->prevvisit=time(NULL); 57 | return 1; 58 | } 59 | *max=LEV_OBSPILOT; 60 | return 0; 61 | } 62 | certificate *getcert(char *cid) 63 | { 64 | certificate *temp; 65 | for (temp=rootcert;temp;temp=temp->next) 66 | if (!STRCASECMP(temp->cid,cid)) return temp; 67 | return NULL; 68 | } 69 | -------------------------------------------------------------------------------- /src/certificate.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "interface.h" 4 | 5 | #ifndef CERTIFICATEHH 6 | #define CERTIFICATEHH 7 | 8 | #define CERTPILOT 1 9 | #define CERTATC 2 10 | 11 | class certificate 12 | { 13 | public: 14 | char *cid, *password, *origin; 15 | int level, livecheck; 16 | time_t prevvisit, creation; 17 | certificate *next, *prev; 18 | certificate(char *, char *, int, time_t, char *); 19 | void configure(char *, int, time_t, char *); 20 | ~certificate(); 21 | }; 22 | extern const char *certlevels[]; 23 | int maxlevel(char *, char *, int *); 24 | certificate *getcert(char *name); 25 | extern certificate *rootcert; 26 | #endif 27 | -------------------------------------------------------------------------------- /src/client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifndef WIN32 4 | #include 5 | #endif 6 | #include 7 | #include 8 | 9 | #include "client.h" 10 | #include "cluser.h" 11 | #include "fsd.h" 12 | #include "support.h" 13 | #include "global.h" 14 | 15 | client *rootclient=NULL; 16 | 17 | client::client(char *i, server *where, char *cs, int t, int reqrating, 18 | char *rev, char *real, int st) 19 | { 20 | next=rootclient, prev=NULL; 21 | if (next) next->prev=this; 22 | rootclient=this; 23 | location=where, cid=strdup(i), type=t, callsign=strdup(cs); 24 | protocol=strdup(rev), sector=NULL, identflag=NULL, facilitytype=0; 25 | rating=reqrating, visualrange=40; 26 | plan=NULL, positionok=0, altitude=0, simtype=st; 27 | realname=strdup(real), starttime=alive=mtime(); 28 | frequency=0; transponder=0; groundspeed=0; lat=0; lon=0; 29 | } 30 | client::~client() 31 | { 32 | serverinterface->clientdropped(this); 33 | if (next) next->prev=prev; 34 | if (prev) prev->next=next; else rootclient=next; 35 | free(cid); 36 | free(callsign); 37 | if (plan) delete plan; 38 | if (realname) free(realname); 39 | if (protocol) free(protocol); 40 | if (sector) free(sector); 41 | if (identflag) free(identflag); 42 | } 43 | void client::handlefp(char **array) 44 | { 45 | int revision=plan?plan->revision+1:0; 46 | if (plan) delete plan; 47 | plan=new flightplan(callsign, *array[0], array[1], 48 | atoi(array[2]), array[3], atoi(array[4]), 49 | atoi(array[5]), array[6], array[7], atoi(array[8]), 50 | atoi(array[9]), atoi(array[10]), atoi(array[11]), array[12], 51 | array[13], array[14]); 52 | plan->revision=revision; 53 | } 54 | /* Update the client fields, given a packet array */ 55 | void client::updatepilot(char **array) 56 | { 57 | unsigned a,b,c; 58 | unsigned x,y,z; 59 | transponder=atoi(array[2]); 60 | if (identflag) free(identflag); 61 | identflag=strdup(array[0]); 62 | sscanf(array[4],"%lf",&lat); 63 | sscanf(array[5],"%lf",&lon); 64 | if (lat>90.0||lat<-90.0||lon>180.0||lon<-180.0) dolog(L_DEBUG, "POSERR: s=(%s,%s) got(%f,%f)", array[4], array[5], lat, lon); 65 | 66 | altitude=atoi(array[6]); 67 | //if (altitude > 100000 || altitude < 0) 68 | // altitude=0; 69 | groundspeed=atoi(array[7]); 70 | pbh=(unsigned int)strtoul(array[8],(char **)NULL,10); 71 | 72 | //x=(pbh&4290772992)>>22; 73 | //y=(pbh&4190208)>>12; 74 | //z=(pbh&4092)>>2; 75 | //dolog(L_INFO,"PBH value in text is %s", array[8]); 76 | //dolog(L_INFO,"PBH unsigned value is %u",pbh); 77 | //dolog(L_INFO,"P=%u B=%u H=%u",x,y,z); 78 | 79 | flags=atoi(array[9]); 80 | setalive(); 81 | positionok=1; 82 | } 83 | void client::updateatc(char **array) 84 | { 85 | int newfreq=atoi(array[0]); 86 | frequency=newfreq; 87 | facilitytype=atoi(array[1]); 88 | visualrange=atoi(array[2]); 89 | sscanf(array[4],"%lf",&lat); 90 | sscanf(array[5],"%lf",&lon); 91 | altitude=atoi(array[6]); 92 | setalive(); 93 | positionok=1; 94 | } 95 | client *getclient(char *ident) 96 | { 97 | client *temp; 98 | for (temp=rootclient;temp;temp=temp->next) 99 | if (!STRCASECMP(ident,temp->callsign)) 100 | return temp; 101 | return NULL; 102 | } 103 | void client::setalive() 104 | { 105 | alive=mtime(); 106 | } 107 | double client::distance(client *other) 108 | { 109 | if (!other) return -1; 110 | if (!positionok||!other->positionok) return -1; 111 | return dist(lat,lon,other->lat,other->lon); 112 | return 1; 113 | } 114 | int client::getrange() 115 | { 116 | if (type==CLIENT_PILOT) 117 | { 118 | if (altitude<0) altitude=0; 119 | return (int) (10+1.414*sqrt((double)altitude)); 120 | } 121 | switch (facilitytype) 122 | { 123 | case 0: return 40; /* Unknown */ 124 | case 1: return 1500; /* FSS */ 125 | case 2: return 5; /* CLR_DEL */ 126 | case 3: return 5; /* GROUND */ 127 | case 4: return 30; /* TOWER */ 128 | case 5: return 100; /* APP/DEP */ 129 | case 6: return 400; /* CENTER */ 130 | case 7: return 1500; /* MONITOR */ 131 | default: return 40; 132 | } 133 | } 134 | 135 | flightplan::flightplan(char *cs, char itype, char *iaircraft, int 136 | itascruise, char *idepairport, int ideptime, int iactdeptime, char *ialt, 137 | char *idestairport, int ihrsenroute, int iminenroute, int ihrsfuel, int 138 | iminfuel, char *ialtairport, char *iremarks, char *iroute) 139 | { 140 | callsign=strdup(cs); 141 | type=itype; 142 | aircraft=strdup(iaircraft); 143 | tascruise=itascruise; 144 | depairport=strdup(idepairport); 145 | deptime=ideptime; 146 | actdeptime=iactdeptime; 147 | alt=strdup(ialt); 148 | destairport=strdup(idestairport); 149 | hrsenroute=ihrsenroute; 150 | minenroute=iminenroute; 151 | hrsfuel=ihrsfuel; 152 | minfuel=iminfuel; 153 | altairport=strdup(ialtairport); 154 | remarks=strdup(iremarks); 155 | route=strdup(iroute); 156 | } 157 | 158 | flightplan::~flightplan() 159 | { 160 | if (callsign) free(callsign); 161 | if (aircraft) free(aircraft); 162 | if (depairport) free(depairport); 163 | if (destairport) free(destairport); 164 | if (alt) free(alt); 165 | if (altairport) free(altairport); 166 | if (remarks) free(remarks); 167 | if (route) free(route); 168 | } 169 | -------------------------------------------------------------------------------- /src/client.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "server.h" 4 | 5 | #ifndef CLIENTHH 6 | #define CLIENTHH 7 | 8 | #define CLIENT_PILOT 1 9 | #define CLIENT_ATC 2 10 | #define CLIENT_ALL 3 11 | 12 | class flightplan 13 | { 14 | public: 15 | char *callsign; 16 | int revision; 17 | char type; 18 | char *aircraft; 19 | int tascruise; 20 | char *depairport; 21 | int deptime; 22 | int actdeptime; 23 | char *alt; 24 | char *destairport; 25 | int hrsenroute, minenroute; 26 | int hrsfuel, minfuel; 27 | char *altairport; 28 | char *remarks; 29 | char *route; 30 | flightplan(char *, char, char *, int, char *, int, int, char *, char *, 31 | int, int, int, int, char *, char *, char *); 32 | ~flightplan(); 33 | }; 34 | class client 35 | { 36 | public: 37 | time_t starttime; 38 | flightplan *plan; 39 | int type, rating; 40 | unsigned int pbh; 41 | int flags; 42 | time_t alive; 43 | char *cid, *callsign, *protocol, *realname, *sector, *identflag; 44 | double lat,lon; 45 | int transponder, altitude, groundspeed, frequency, facilitytype; 46 | int positionok, visualrange, simtype; 47 | server *location; 48 | client *next, *prev; 49 | client(char *, server *, char *, int, int, char *, char *, int); 50 | ~client(); 51 | void updatepilot(char **); 52 | void updateatc(char **); 53 | void handlefp(char **); 54 | void setalive(); 55 | double distance(client *); 56 | int getrange(); 57 | }; 58 | extern client *rootclient; 59 | client *getclient(char *); 60 | #endif 61 | -------------------------------------------------------------------------------- /src/clinterface.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifndef WIN32 4 | #include 5 | #endif 6 | #include 7 | #include 8 | 9 | #include "interface.h" 10 | #include "clinterface.h" 11 | #include "global.h" 12 | #include "support.h" 13 | #include "server.h" 14 | #include "protocol.h" 15 | #include "client.h" 16 | #include "cluser.h" 17 | #include "user.h" 18 | 19 | /* The client interface class */ 20 | 21 | clinterface::clinterface(int port, char *code, char *d): 22 | tcpinterface(port, code, d) 23 | { 24 | prevwinddelta=mtime(); 25 | } 26 | int clinterface::run() 27 | { 28 | int busy=tcpinterface::run(); 29 | if ((mtime()-prevwinddelta)>WINDDELTATIMEOUT) 30 | { 31 | prevwinddelta=mtime(); 32 | sendwinddelta(); 33 | } 34 | 35 | return busy; 36 | } 37 | 38 | void clinterface::newuser(int fd, char *peer, int portnum, int g) 39 | { 40 | insertuser(new cluser(fd,this,peer,portnum,g)); 41 | } 42 | 43 | void clinterface::sendaa(client *who, absuser *ex) 44 | { 45 | char data[1000]; 46 | sprintf(data,"%s:SERVER:%s:%s::%d", who->callsign, who->realname, 47 | who->cid,who->rating,who->protocol); 48 | sendpacket(NULL, NULL, ex, CLIENT_ALL, -1, CL_ADDATC, data); 49 | } 50 | void clinterface::sendap(client *who, absuser *ex) 51 | { 52 | char data[1000]; 53 | sprintf(data,"%s:SERVER:%s::%d:%s:%d",who->callsign,who->cid,who->rating, 54 | who->protocol,who->simtype); 55 | sendpacket(NULL, NULL, ex, CLIENT_ALL, -1, CL_ADDPILOT, data); 56 | } 57 | void clinterface::sendda(client *who, absuser *ex) 58 | { 59 | char data[1000]; 60 | sprintf(data,"%s:%s",who->callsign,who->cid); 61 | sendpacket(NULL, NULL, ex, CLIENT_ALL, -1, CL_RMATC, data); 62 | } 63 | void clinterface::senddp(client *who, absuser *ex) 64 | { 65 | char data[1000]; 66 | sprintf(data,"%s:%s",who->callsign,who->cid); 67 | sendpacket(NULL, NULL, ex, CLIENT_ALL, -1, CL_RMPILOT, data); 68 | } 69 | void clinterface::sendweather(client *who, wprofile *p) 70 | { 71 | int x; 72 | char buf[1000], part[200]; 73 | p->fix(who->lat, who->lon); 74 | sprintf(buf,"%s:%s", "server", who->callsign); 75 | for (x=0;x<4;x++) 76 | { 77 | templayer *l=&p->temps[x]; 78 | sprintf(part, ":%d:%d", l->ceiling, l->temp); 79 | strcat(buf, part); 80 | } 81 | sprintf(part,":%d", p->barometer); 82 | strcat(buf, part); 83 | sendpacket(who, NULL, NULL, CLIENT_ALL, -1, CL_TEMPDATA, buf); 84 | 85 | sprintf(buf,"%s:%s", "server", who->callsign); 86 | for (x=0;x<4;x++) 87 | { 88 | windlayer *l=&p->winds[x]; 89 | sprintf(part,":%d:%d:%d:%d:%d:%d", l->ceiling, l->floor, l->direction, 90 | l->speed, l->gusting, l->turbulence); 91 | strcat(buf, part); 92 | } 93 | sendpacket(who, NULL, NULL, CLIENT_ALL, -1, CL_WINDDATA, buf); 94 | 95 | sprintf(buf,"%s:%s", "server", who->callsign); 96 | for (x=0;x<3;x++) 97 | { 98 | cloudlayer *c=(x==2?p->tstorm:&p->clouds[x]); 99 | sprintf(part,":%d:%d:%d:%d:%d", c->ceiling, c->floor, c->coverage, 100 | c->icing, c->turbulence); 101 | strcat(buf, part); 102 | } 103 | sprintf(part,":%.2f", p->visibility); 104 | strcat(buf, part); 105 | sendpacket(who, NULL, NULL, CLIENT_ALL, -1, CL_CLOUDDATA, buf); 106 | } 107 | void clinterface::sendmetar(client *who, char *data) 108 | { 109 | char buf[1000]; 110 | sprintf(buf,"server:%s:METAR:%s", who->callsign, data); 111 | sendpacket(who, NULL, NULL, CLIENT_ALL, -1, CL_REPACARS, buf); 112 | } 113 | void clinterface::sendnowx(client *who, char *station) 114 | { 115 | absuser *temp; 116 | for (temp=rootuser;temp;temp=temp->next) 117 | { 118 | cluser *ctemp=(cluser *)temp; 119 | if (ctemp->thisclient==who) 120 | { 121 | ctemp->showerror(ERR_NOWEATHER, station); 122 | break; 123 | } 124 | } 125 | } 126 | int clinterface::getbroad(char *s) 127 | { 128 | int broad=CLIENT_ALL; 129 | if (!strcmp(s,"*P")) broad=CLIENT_PILOT; else 130 | if (!strcmp(s,"*A")) broad=CLIENT_ATC; 131 | return broad; 132 | } 133 | void clinterface::sendgeneric(char *to, client *dest, absuser *ex, 134 | client *source, char *from, char *s, int cmd) 135 | { 136 | char buf[1000]; 137 | int range=-1; 138 | sprintf(buf,"%s:%s:%s",from,to,s); 139 | if (to[0]=='@'&&source) 140 | range=source->getrange(); 141 | sendpacket(dest, source, ex, getbroad(to), range, cmd, buf); 142 | } 143 | void clinterface::sendpilotpos(client *who, absuser *ex) 144 | { 145 | char data[1000]; 146 | sprintf(data,"%s:%s:%d:%d:%.5f:%.5f:%d:%d:%u:%d", who->identflag, 147 | who->callsign, who->transponder, who->rating, who->lat, who->lon, 148 | who->altitude, who->groundspeed, who->pbh, who->flags); 149 | //dolog(L_INFO,"PBH unsigned value is %u",who->pbh); 150 | //dolog(L_INFO,"SendPilotPos is sending: %s",data); 151 | sendpacket(NULL, who, ex, CLIENT_ALL, -1, CL_PILOTPOS, data); 152 | } 153 | void clinterface::sendatcpos(client *who, absuser *ex) 154 | { 155 | char data[1000]; 156 | sprintf(data,"%s:%d:%d:%d:%d:%.5f:%.5f:%d",who->callsign, 157 | who->frequency, who->facilitytype, who->visualrange, who->rating, 158 | who->lat, who->lon, who->altitude); 159 | sendpacket(NULL, who, ex, CLIENT_ALL, -1, CL_ATCPOS, data); 160 | } 161 | 162 | void clinterface::sendplan(client *dest, client *who, int range) 163 | { 164 | char buf[1000], *cs=(char *)(dest?dest->callsign:"*A"); 165 | flightplan *plan=who->plan; 166 | sprintf(buf,"%s:%s:%c:%s:%d:%s:%d:%d:%s:%s:%d:%d:%d:%d:%s:%s:%s", 167 | who->callsign, cs, plan->type, plan->aircraft, 168 | plan->tascruise, plan->depairport, plan->deptime, plan->actdeptime, 169 | plan->alt, plan->destairport, plan->hrsenroute, plan->minenroute, 170 | plan->hrsfuel, plan->minfuel, plan->altairport, plan->remarks, 171 | plan->route); 172 | sendpacket(dest, NULL, NULL, CLIENT_ATC, range, CL_PLAN, buf); 173 | } 174 | void clinterface::handlekill(client *who, char *reason) 175 | { 176 | if (who->location!=myserver) return; 177 | absuser *temp; 178 | for (temp=rootuser;temp;temp=temp->next) 179 | { 180 | cluser *ctemp=(cluser*)temp; 181 | if (ctemp->thisclient==who) 182 | { 183 | char buf[1000]; 184 | sprintf(buf,"SERVER:%s:%s", who->callsign, reason); 185 | sendpacket(who, NULL, NULL, CLIENT_ALL, -1, CL_KILL, buf); 186 | temp->kill(KILL_KILL); 187 | } 188 | } 189 | } 190 | void clinterface::sendwinddelta() 191 | { 192 | msrand(time(NULL)); 193 | char buf[100]; 194 | int speed=mrand()%11-5; 195 | int direction=mrand()%21-10; 196 | sprintf(buf,"SERVER:*:%d:%d", speed, direction); 197 | sendpacket(NULL, NULL, NULL, CLIENT_ALL, -1, CL_WDELTA, buf); 198 | } 199 | 200 | int clinterface::calcrange(client *from, client *to, int type, int range) 201 | { 202 | int x, y; 203 | switch (type) 204 | { 205 | case CL_PILOTPOS: 206 | case CL_ATCPOS: 207 | if (to->type==CLIENT_ATC) return to->visualrange; 208 | x=to->getrange(), y=from->getrange(); 209 | if (from->type==CLIENT_PILOT) return x+y; 210 | return x>y?x:y; 211 | case CL_MESSAGE: 212 | x=to->getrange(), y=from->getrange(); 213 | if (from->type==CLIENT_PILOT&&to->type==CLIENT_PILOT) return x+y; 214 | return x>y?x:y; 215 | default : return range; 216 | } 217 | } 218 | 219 | /* Send a packet to a client. 220 | If is specified, only the client will receive the message. 221 | indicates if only pilot, only atc, or both will receive the 222 | message 223 | */ 224 | void clinterface::sendpacket(client *dest, client *source, absuser *exclude, 225 | int broad, int range, int cmd, char *data) 226 | { 227 | absuser *temp; 228 | if (dest) if (dest->location!=myserver) return; 229 | for (temp=rootuser;temp;temp=temp->next) if (!temp->killflag) 230 | { 231 | client *cl=((cluser*)temp)->thisclient; 232 | if (!cl) continue; 233 | if (exclude==temp) continue; 234 | if (dest&&cl!=dest) continue; 235 | if (!(cl->type&broad)) continue; 236 | if (source&&(range!=-1||cmd==CL_PILOTPOS||cmd==CL_ATCPOS)) 237 | { 238 | int checkrange=calcrange(source, cl, cmd, range); 239 | double distance=cl->distance(source); 240 | if (distance==-1||distance>checkrange) continue; 241 | } 242 | temp->uslprintf("%s%s\r\n", cmd==CL_ATCPOS||cmd==CL_PILOTPOS, 243 | clcmdnames[cmd], data); 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /src/clinterface.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "interface.h" 4 | #include "user.h" 5 | #include "client.h" 6 | #include "wprofile.h" 7 | 8 | #ifndef CLINTERFACEHH 9 | #define CLINTERFACEHH 10 | 11 | class clinterface:public tcpinterface 12 | { 13 | time_t prevwinddelta; 14 | int getbroad(char *); 15 | public: 16 | void sendaa(client *, absuser *); 17 | void sendap(client *, absuser *); 18 | void sendda(client *, absuser *); 19 | void senddp(client *, absuser *); 20 | void sendgeneric(char *, client *, absuser *, client *, char *, char *, int); 21 | void sendpilotpos(client *, absuser *); 22 | void sendatcpos(client *, absuser *); 23 | void sendplan(client *, client *, int); 24 | void sendweather(client *, wprofile *); 25 | void sendmetar(client *, char *); 26 | void sendnowx(client *, char *); 27 | void sendpacket(client *, client *, absuser *, int, int, int, char *); 28 | void sendwinddelta(); 29 | void handlekill(client *, char *); 30 | int calcrange(client *, client *, int, int); 31 | clinterface(int, char *, char *); 32 | virtual int run(); 33 | virtual void newuser(int, char *, int, int); 34 | friend class cluser; 35 | }; 36 | #endif 37 | -------------------------------------------------------------------------------- /src/cluser.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifndef WIN32 4 | #include 5 | #endif 6 | #include 7 | #include 8 | 9 | #include "global.h" 10 | #include "cluser.h" 11 | #include "support.h" 12 | #include "server.h" 13 | #include "fsd.h" 14 | #include "protocol.h" 15 | #include "mm.h" 16 | #include "fsdpaths.h" 17 | 18 | /* The client communication command names */ 19 | const char *clcmdnames[]= 20 | { 21 | "#AA", 22 | "#DA", 23 | "#AP", 24 | "#DP", 25 | "$HO", 26 | "#TM", 27 | "#RW", 28 | "@", 29 | "%", 30 | "$PI", 31 | "$PO", 32 | "$HA", 33 | "$FP", 34 | "#SB", 35 | "#PC", 36 | "#WX", 37 | "#CD", 38 | "#WD", 39 | "#TD", 40 | "$C?", 41 | "$CI", 42 | "$AX", 43 | "$AR", 44 | "$ER", 45 | "$CQ", 46 | "$CR", 47 | "$!!", 48 | "#DL", 49 | NULL 50 | }; 51 | 52 | const char *errstr[]= 53 | { 54 | "No error", 55 | "Callsign in use", 56 | "Invalid callsign", 57 | "Already registerd", 58 | "Syntax error", 59 | "Invalid source callsign", 60 | "Invalid CID/password", 61 | "No such callsign", 62 | "No flightplan", 63 | "No such weather profile", 64 | "Invalid protocol revision", 65 | "Requested level too high", 66 | "Too many clients connected", 67 | "CID/PID was suspended" 68 | }; 69 | 70 | /* The client user */ 71 | cluser::cluser(int fd, clinterface *p, char *pn, int portnum, int gg): 72 | absuser(fd,p,pn,portnum, gg) 73 | { 74 | parent=p; 75 | thisclient=NULL; 76 | configgroup *gu=configman->getgroup("system"); 77 | configentry *e=gu?gu->getentry("maxclients"):(configentry*)NULL; 78 | int total=manager->getvar(p->varcurrent)->value.number; 79 | if (e&&atoi(e->getdata())<=total) 80 | { 81 | showerror(ERR_SERVFULL, ""); 82 | kill(KILL_COMMAND); 83 | } 84 | } 85 | cluser::~cluser() 86 | { 87 | if (thisclient) 88 | { 89 | int type=thisclient->type; 90 | serverinterface->sendrmclient(NULL,"*",thisclient, this); 91 | delete thisclient; 92 | } 93 | } 94 | void cluser::readmotd() 95 | { 96 | FILE *io=fopen(PATH_FSD_MOTD,"r"); 97 | char line[1000]; 98 | sprintf(line, "%s", PRODUCT); 99 | clientinterface->sendgeneric(thisclient->callsign, thisclient, NULL, 100 | NULL, "server", line, CL_MESSAGE); 101 | if (!io) return; 102 | while (fgets(line,1000,io)) 103 | { 104 | line[strlen(line)-1]='\0'; 105 | clientinterface->sendgeneric(thisclient->callsign, thisclient, NULL, 106 | NULL, "server", line, CL_MESSAGE); 107 | } 108 | fclose(io); 109 | } 110 | void cluser::parse(char *s) 111 | { 112 | setactive(); 113 | doparse(s); 114 | } 115 | /* Checks if the given callsign is OK. returns 0 on success or errorcode 116 | on failure */ 117 | int cluser::callsignok(char *name) 118 | { 119 | client *temp; 120 | if (strlen(name)<2||strlen(name)>CALLSIGNBYTES) return ERR_CSINVALID; 121 | if (strpbrk(name, "!@#$%*:& \t")) return ERR_CSINVALID; 122 | for (temp=rootclient;temp;temp=temp->next) 123 | if (!STRCASECMP(temp->callsign,name)) return ERR_CSINUSE; 124 | return ERR_OK; 125 | } 126 | int cluser::checksource(char *from) 127 | { 128 | if (STRCASECMP(from,thisclient->callsign)) 129 | { 130 | showerror(ERR_SRCINVALID, from); 131 | return 0; 132 | } 133 | return 1; 134 | } 135 | int cluser::getcomm(char *cmd) 136 | { 137 | int index; 138 | for (index=0;clcmdnames[index];index++) 139 | if (!strncmp(cmd,clcmdnames[index],strlen(clcmdnames[index]))) 140 | return index; 141 | return -1; 142 | } 143 | int cluser::showerror(int num, char *env) 144 | { 145 | uprintf("$ERserver:%s:%03d:%s:%s\r\n",thisclient?thisclient->callsign: 146 | "unknown",num,env,errstr[num]); 147 | return num; 148 | } 149 | int cluser::checklogin(char *id, char *pwd, int req) 150 | { 151 | if (id[0]=='\0') return -2; 152 | int max, ok=maxlevel(id, pwd, &max); 153 | if (!ok) 154 | { 155 | showerror(ERR_CIDINVALID, id); 156 | return -1; 157 | } 158 | return req>max?max:req; 159 | } 160 | void cluser::execaa(char **s, int count) 161 | { 162 | if (thisclient) 163 | { 164 | showerror(ERR_REGISTERED, ""); 165 | return; 166 | } 167 | if (count<7) 168 | { 169 | showerror(ERR_SYNTAX, ""); 170 | return; 171 | } 172 | int err=callsignok(s[0]); 173 | if (err) 174 | { 175 | showerror(err, ""); 176 | kill(KILL_COMMAND); 177 | return; 178 | } 179 | if (atoi(s[6])!=NEEDREVISION) 180 | { 181 | showerror(ERR_REVISION, ""); 182 | kill(KILL_PROTOCOL); 183 | return; 184 | } 185 | int req=atoi(s[5]); 186 | if (req<0) req=0; 187 | int level=checklogin(s[3], s[4], req); 188 | if (level==0) 189 | { 190 | showerror(ERR_CSSUSPEND, ""); 191 | kill(KILL_COMMAND); 192 | return; 193 | } 194 | else if (level==-1) 195 | { 196 | kill(KILL_COMMAND); 197 | return; 198 | } 199 | else if (level==-2) level=1; 200 | if (levelsendaddclient("*",thisclient, NULL, this, 0); 209 | readmotd(); 210 | } 211 | void cluser::execap(char **s, int count) 212 | { 213 | if (thisclient) 214 | { 215 | showerror(ERR_REGISTERED, ""); 216 | return; 217 | } 218 | if (count<8) 219 | { 220 | showerror(ERR_SYNTAX, ""); 221 | return; 222 | } 223 | int err=callsignok(s[0]); 224 | if (err) 225 | { 226 | showerror(err, ""); 227 | kill(KILL_COMMAND); 228 | return; 229 | } 230 | if (atoi(s[5])!=NEEDREVISION) 231 | { 232 | showerror(ERR_REVISION, ""); 233 | kill(KILL_PROTOCOL); 234 | return; 235 | } 236 | int req=atoi(s[4]); 237 | if (req<0) req=0; 238 | int level=checklogin(s[2], s[3], req); 239 | if (level<0) 240 | { 241 | kill(KILL_COMMAND); 242 | return; 243 | } 244 | else if (level==0) 245 | { 246 | showerror(ERR_CSSUSPEND, ""); 247 | kill(KILL_COMMAND); 248 | return; 249 | } 250 | if (levelsendaddclient("*",thisclient, NULL, this, 0); 259 | readmotd(); 260 | } 261 | void cluser::execmulticast(char **s, int count, int cmd, int nargs, int multiok) 262 | { 263 | nargs+=2; 264 | if (countsendmulticast(thisclient, to, data, cmd, multiok, this); 274 | } 275 | void cluser::execd(char **s, int count) 276 | { 277 | if (count==0) 278 | { 279 | showerror(ERR_SYNTAX, ""); 280 | return; 281 | } 282 | if (!checksource(s[0])) return; 283 | kill(KILL_COMMAND); 284 | } 285 | void cluser::execpilotpos(char **array, int count) 286 | { 287 | if (count<10) 288 | { 289 | showerror(ERR_SYNTAX, ""); 290 | return; 291 | } 292 | if (!checksource(array[1])) return; 293 | thisclient->updatepilot(array); 294 | serverinterface->sendpilotdata(thisclient, this); 295 | } 296 | void cluser::execatcpos(char **array, int count) 297 | { 298 | if (count<8) 299 | { 300 | showerror(ERR_SYNTAX, ""); 301 | return; 302 | } 303 | if (!checksource(array[0])) return; 304 | thisclient->updateatc(array+1); 305 | serverinterface->sendatcdata(thisclient, this); 306 | } 307 | void cluser::execfp(char **array, int count) 308 | { 309 | if (count<17) 310 | { 311 | showerror(ERR_SYNTAX, ""); 312 | return; 313 | } 314 | if (!checksource(array[0])) return; 315 | thisclient->handlefp(array+2); 316 | serverinterface->sendplan("*", thisclient, NULL); 317 | } 318 | void cluser::execweather(char **array, int count) 319 | { 320 | if (count<3) 321 | { 322 | showerror(ERR_SYNTAX, ""); 323 | return; 324 | } 325 | if (!checksource(array[0])) return; 326 | char source[CALLSIGNBYTES+2]; 327 | sprintf(source, "%%%s", thisclient->callsign); 328 | metarmanager->requestmetar(source, array[2], 1, -1); 329 | } 330 | void cluser::execacars(char **array, int count) 331 | { 332 | if (count<3) 333 | { 334 | showerror(ERR_SYNTAX, ""); 335 | return; 336 | } 337 | if (!checksource(array[0])) return; 338 | if (!STRCASECMP(array[2],"METAR")&&count>3) 339 | { 340 | char source[CALLSIGNBYTES+2]; 341 | sprintf(source, "%%%s", thisclient->callsign); 342 | metarmanager->requestmetar(source, array[3], 0, -1); 343 | } 344 | } 345 | void cluser::execcq(char **array, int count) 346 | { 347 | if (count < 3 ) 348 | { 349 | showerror(ERR_SYNTAX, ""); 350 | return; 351 | } 352 | if (STRCASECMP(array[1], "server")) 353 | { 354 | execmulticast(array, count, CL_CQ, 1, 1); 355 | return; 356 | } 357 | if (!STRCASECMP(strupr(array[2]), "RN")) 358 | { 359 | client *cl=getclient(array[1]); 360 | if (cl) 361 | { 362 | char data[1000]; 363 | sprintf(data, "%s:%s:RN:%s:USER:%d", cl->callsign, thisclient->callsign, cl->realname,cl->rating); 364 | clientinterface->sendpacket(thisclient,cl,NULL,CLIENT_ALL,-1,CL_CR,data); 365 | return; 366 | } 367 | } 368 | if (!STRCASECMP(array[2], "fp")) 369 | { 370 | client *cl=getclient(array[3]); 371 | if (!cl) 372 | { 373 | showerror(ERR_NOSUCHCS, array[3]); 374 | return; 375 | } 376 | if (!cl->plan) 377 | { 378 | showerror(ERR_NOFP, ""); 379 | return; 380 | } 381 | clientinterface->sendplan(thisclient, cl, -1); 382 | return; 383 | } 384 | } 385 | 386 | void cluser::execkill(char ** array, int count) 387 | { 388 | char junk[64]; 389 | if (count < 3 ) 390 | { 391 | showerror(ERR_SYNTAX, ""); 392 | return; 393 | } 394 | client *cl=getclient(array[1]); 395 | if (!cl) 396 | { 397 | showerror(ERR_NOSUCHCS, array[1]); 398 | return; 399 | } 400 | 401 | if (thisclient->rating<11) 402 | { 403 | sprintf(junk, "You are not allowed to kill users!"); 404 | clientinterface->sendgeneric(thisclient->callsign, thisclient, NULL, 405 | NULL, "server", junk, CL_MESSAGE); 406 | sprintf(junk,"%s attempted to remove %s, but was not allowed to",thisclient->callsign,array[1]); 407 | dolog(L_ERR,junk); 408 | } 409 | else 410 | { 411 | sprintf(junk, "Attempting to kill %s", array[1]); 412 | clientinterface->sendgeneric(thisclient->callsign, thisclient, NULL, 413 | NULL, "server", junk, CL_MESSAGE); 414 | sprintf(junk,"%s Killed %s",thisclient->callsign,array[1]); 415 | dolog(L_INFO,junk); 416 | serverinterface->sendkill(cl,array[2]); 417 | } 418 | return; 419 | } 420 | void cluser::doparse(char *s) 421 | { 422 | char cmd[4], *array[100]; 423 | snappacket(s, cmd, 3); 424 | int index=getcomm(cmd), count; 425 | if (index==-1) 426 | { 427 | showerror(ERR_SYNTAX, ""); 428 | return; 429 | } 430 | if (!thisclient&&index!=CL_ADDATC&&index!=CL_ADDPILOT) return; 431 | 432 | /* Just a hack to put the pointer on the first arg here */ 433 | s+=strlen(clcmdnames[index]); 434 | count=breakpacket(s,array,100); 435 | switch (index) 436 | { 437 | case CL_ADDATC : execaa(array,count); break; 438 | case CL_ADDPILOT : execap(array,count); break; 439 | case CL_PLAN : execfp(array,count); break; 440 | case CL_RMATC : /* Handled like RMPILOT */ 441 | case CL_RMPILOT : execd(array,count); break; 442 | case CL_PILOTPOS : execpilotpos(array,count); break; 443 | case CL_ATCPOS : execatcpos(array,count); break; 444 | case CL_PONG : 445 | case CL_PING : execmulticast(array,count,index,0,1); break; 446 | case CL_MESSAGE : execmulticast(array,count,index,1,1); break; 447 | case CL_REQHANDOFF : 448 | case CL_ACHANDOFF : execmulticast(array,count,index,1,0); break; 449 | case CL_SB : 450 | case CL_PC : execmulticast(array,count,index,0,0); break; 451 | case CL_WEATHER : execweather(array, count); break; 452 | case CL_REQCOM : execmulticast(array,count,index,0,0); break; 453 | case CL_REPCOM : execmulticast(array,count,index,1,0); break; 454 | case CL_REQACARS : execacars(array, count); break; 455 | case CL_CR : execmulticast(array, count, index, 2, 0); break; 456 | case CL_CQ : execcq(array, count); break; 457 | case CL_KILL : execkill(array, count); break; 458 | default : showerror(ERR_SYNTAX, ""); break; 459 | } 460 | } 461 | -------------------------------------------------------------------------------- /src/cluser.h: -------------------------------------------------------------------------------- 1 | #include "interface.h" 2 | #include "clinterface.h" 3 | #include "user.h" 4 | #include "client.h" 5 | 6 | #ifndef CLUSERHH 7 | #define CLUSERHH 8 | 9 | class cluser : public absuser 10 | { 11 | clinterface *parent; 12 | void doparse(char *s); 13 | int getcomm(char *); 14 | int callsignok(char *name); 15 | int showerror(int, char *); 16 | int checksource(char *); 17 | int checklogin(char *, char *, int); 18 | void execd(char **, int); 19 | void execaa(char **, int); 20 | void execap(char **, int); 21 | void execmulticast(char **, int, int, int, int); 22 | void execpilotpos(char **, int); 23 | void execatcpos(char **, int); 24 | void execfp(char **, int); 25 | void execweather(char **, int); 26 | void execacars(char **, int); 27 | void execcq(char **, int); 28 | void execkill(char **, int); 29 | void readmotd(); 30 | public: 31 | 32 | client *thisclient; 33 | cluser(int, clinterface *, char *, int, int); 34 | virtual void parse (char *); 35 | cluser(user *); 36 | virtual ~cluser(); 37 | friend class clinterface; 38 | }; 39 | #endif 40 | -------------------------------------------------------------------------------- /src/config.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "manage.h" 8 | #include "config.h" 9 | #include "support.h" 10 | #include "global.h" 11 | 12 | configentry::configentry(char *v, char *d) 13 | { 14 | var=strdup(v), data=strdup(d), changed=1, parts=NULL, partbuf=NULL; 15 | } 16 | configentry::~configentry() 17 | { 18 | free(var); 19 | free(data); 20 | if (parts) free(parts); 21 | if (partbuf) free(partbuf); 22 | } 23 | char *configentry::getdata() 24 | { 25 | return data; 26 | } 27 | void configentry::setdata(char *d) 28 | { 29 | free(data); 30 | data=strdup(d); 31 | changed=1; 32 | if (parts) free(parts); 33 | if (partbuf) free(partbuf); 34 | parts=NULL, partbuf=NULL; 35 | } 36 | int configentry::getint() 37 | { 38 | return atoi(data); 39 | } 40 | 41 | void configentry::fillparts() 42 | { 43 | nparts=0; 44 | if (parts) free(parts); 45 | if (partbuf) free(partbuf); 46 | parts=NULL; 47 | partbuf=strdup(data); 48 | char *p=partbuf; 49 | while (p) 50 | { 51 | char *end=strchr(p, ','); 52 | if (end) 53 | { 54 | char *seek=end-1; 55 | *(end++)='\0'; 56 | while (isspace(*seek)) *(seek--)='\0'; 57 | } 58 | while (isspace(p[0])) p++; 59 | if (parts) parts=(char **)realloc(parts,sizeof(char*)*++nparts); 60 | else parts=(char **)malloc(sizeof(char*)*(nparts=1)); 61 | parts[nparts-1]=p; 62 | p=end; 63 | } 64 | } 65 | int configentry::inlist(char *entry) 66 | { 67 | int x; 68 | if (!parts) fillparts(); 69 | for (x=0;x=nparts) return NULL; 81 | return parts[num]; 82 | } 83 | configgroup::configgroup(char *n) 84 | { 85 | name=strdup(n); 86 | entries=NULL, nentries=0, changed=1; 87 | } 88 | configgroup::~configgroup() 89 | { 90 | int x; 91 | for (x=0;xvar)) 99 | return entries[x]; 100 | return NULL; 101 | } 102 | configentry *configgroup::createentry(char *var, char *data) 103 | { 104 | if (entries) 105 | entries=(configentry**)realloc(entries, sizeof(configentry *)*++nentries); 106 | else entries=(configentry**)malloc(sizeof(configentry *)), nentries=1; 107 | return entries[nentries-1]=new configentry(var, data); 108 | } 109 | void configgroup::handleentry(char *var, char *data) 110 | { 111 | configentry *e=getentry(var); 112 | if (!e) 113 | { 114 | createentry(var, data); 115 | changed=1; 116 | return; 117 | } 118 | if (!strcmp(e->data, data)) return; 119 | e->setdata(data); 120 | changed=1; 121 | return; 122 | } 123 | 124 | 125 | 126 | configmanager::configmanager(char *name) 127 | { 128 | filename=strdup(name); 129 | groups=NULL, ngroups=0, changed=1; 130 | int fname=manager->addvar("config.filename", ATT_VARCHAR); 131 | manager->setvar(fname, name); 132 | varaccess=manager->addvar("config.lastread", ATT_DATE); 133 | parsefile(); 134 | } 135 | configmanager::~configmanager() 136 | { 137 | int x; 138 | for (x=0;xname)) 146 | return groups[x]; 147 | return NULL; 148 | } 149 | configgroup *configmanager::creategroup(char *name) 150 | { 151 | if (groups) 152 | groups=(configgroup**)realloc(groups, sizeof(configgroup *)*++ngroups); 153 | else groups=(configgroup**)malloc(sizeof(configgroup *)), ngroups=1; 154 | return groups[ngroups-1]=new configgroup(name); 155 | } 156 | int configmanager::run() 157 | { 158 | if ((mtime()-prevcheck)setvar(varaccess, time(NULL)); 173 | configgroup *current=NULL; 174 | while (fgets(line, 300, io)) 175 | { 176 | if (line[0]=='#'||line[0]=='\n'||line[0]=='\r') continue; 177 | if (line[0]=='[') 178 | { 179 | char *p=strchr(line, ']'); 180 | if (p) *p='\0'; 181 | current=getgroup(line+1); 182 | if (!current) current=creategroup(line+1); 183 | continue; 184 | } 185 | if (!current) continue; 186 | line[strlen(line)-1]='\0'; 187 | char *sep=strchr(line,'='); 188 | if (!sep) continue; 189 | *(sep++)='\0'; 190 | while (isspace(*sep)) sep++; 191 | if (sep) current->handleentry(line, sep); 192 | if (current->changed) changed=1; 193 | } 194 | fclose(io); 195 | prevcheck=mtime(); 196 | } 197 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "process.h" 3 | 4 | #ifndef CONFIGHH 5 | #define CONFIGHH 6 | 7 | #define CONFIGINTERVAL 10 8 | 9 | class configmanager; 10 | class configentry 11 | { 12 | char *var, *data, **parts, *partbuf; 13 | int changed, nparts; 14 | void fillparts(); 15 | void setdata(char *); 16 | public: 17 | configentry(char *, char *); 18 | ~configentry(); 19 | char *getdata(); 20 | int getint(); 21 | int inlist(char *); 22 | int getnparts(); 23 | char *getpart(int); 24 | friend class configgroup; 25 | }; 26 | 27 | class configgroup 28 | { 29 | char *name; 30 | void handleentry(char *, char *); 31 | configentry **entries; 32 | int nentries; 33 | 34 | public: 35 | int changed; 36 | configgroup(char *); 37 | ~configgroup(); 38 | configentry *getentry(char *); 39 | configentry *createentry(char *, char *); 40 | friend class configmanager; 41 | }; 42 | 43 | class configmanager : public process 44 | { 45 | char *filename; 46 | configgroup **groups; 47 | int ngroups; 48 | int varaccess; 49 | int changed; 50 | time_t prevcheck, lastmodify; 51 | configgroup *creategroup(char *); 52 | void parsefile(); 53 | 54 | public: 55 | configmanager(char *); 56 | ~configmanager(); 57 | virtual int run(); 58 | configgroup *getgroup(char *); 59 | }; 60 | 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /src/fsd.h: -------------------------------------------------------------------------------- 1 | #ifndef FSDHH 2 | #define FSDHH 3 | #include "servinterface.h" 4 | #include "clinterface.h" 5 | #include "sysinterface.h" 6 | #include "interface.h" 7 | #include "process.h" 8 | #include "config.h" 9 | #include 10 | 11 | class fsd 12 | { 13 | int clientport, serverport, systemport; 14 | pman *pmanager; 15 | char *certfile; 16 | char *whazzupfile; 17 | char *whazzupjsonfile; 18 | time_t timer, prevnotify, prevlagcheck, certfilestat, prevcertcheck; 19 | void configmyserver(); 20 | void configure(); 21 | void createmanagevars(); 22 | void createinterfaces(); 23 | void readcert(); 24 | void handlecidline(char *); 25 | void makeconnections(); 26 | void dochecks(); 27 | void initdb(); 28 | time_t prevwhazzup; 29 | int fileopen; 30 | public: 31 | fsd(char *); 32 | ~fsd(); 33 | void run(); 34 | }; 35 | extern clinterface *clientinterface; 36 | extern servinterface *serverinterface; 37 | extern sysinterface *systeminterface; 38 | extern configmanager *configman; 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/fsdpaths.h: -------------------------------------------------------------------------------- 1 | #ifdef __GNUC__ 2 | #ident "$Id: fsdpaths.h,v 2.01 1998/11/19 10:18:14 marty Exp $" 3 | #endif 4 | #ifndef FSDPATHSHH 5 | #define FSDPATHSHH 6 | 7 | #define PATH_FSD_CONF "fsd.conf" 8 | #define PATH_FSD_HELP "help.txt" 9 | #define PATH_FSD_MOTD "motd.txt" 10 | #define LOGFILE "log.txt" 11 | #define METARFILE "metar.txt" 12 | #define METARFILENEW "metarnew.txt" 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/global.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "manage.h" 4 | #include "fsdpaths.h" 5 | 6 | #ifndef GLOBALHH 7 | #define GLOBALHH 8 | 9 | #ifdef WIN32 10 | #define STRCASECMP(a,b) _stricmp(a,b) 11 | #define WRITESOCK(a,b,c) send(a,b,c,0) 12 | #define READSOCK(a,b,c) recv(a,b,c,0) 13 | #define socklen_t int 14 | #define CLOSESOCKET(a) closesocket(a) 15 | #else 16 | #define STRCASECMP(a,b) strcasecmp(a,b) 17 | #define WRITESOCK(a,b,c) write(a,b,c) 18 | #define READSOCK(a,b,c) read(a,b,c) 19 | #define CLOSESOCKET(a) close(a) 20 | #endif 21 | 22 | #define PRODUCT "BetterFSD Version0.3 By 4185 QQ:3174327625" 23 | #define VERSION "V0.3" 24 | #define NEEDREVISION 9 25 | 26 | /* 27 | WARNING!!!: The USERTIMEOUT (idle time of a SOCKET before it's dropped) 28 | should not be higher than the SERVERTIMEOUT (idle time of a 29 | server) 30 | */ 31 | #define USERTIMEOUT 500 32 | #define SERVERTIMEOUT 800 33 | #define CLIENTTIMEOUT 800 34 | #define SILENTCLIENTTIMEOUT 36000 35 | #define WINDDELTATIMEOUT 70 36 | 37 | #define USERPINGTIMEOUT 200 38 | #define USERFEEDCHECK 3 39 | #define LAGCHECK 60 40 | #define NOTIFYCHECK 300 41 | #define SYNCTIMEOUT 120 42 | #define SERVERMAXTOOK 240 43 | #define MAXHOPS 10 44 | #define GUARDRETRY 120 45 | #define CALLSIGNBYTES 12 46 | #define MAXLINELENGTH 512 47 | #define MAXMETARDOWNLOADTIME 1600 48 | #define CERTFILECHECK 30 49 | 50 | #define WHAZZUPCHECK 1 51 | #define CONNECTDELAY 20 52 | 53 | #define LEV_SUSPENDED 0 54 | #define LEV_OBSPILOT 1 55 | #define LEV_STUDENT1 2 56 | #define LEV_STUDENT2 3 57 | #define LEV_STUDENT3 4 58 | #define LEV_CONTROLLER1 5 59 | #define LEV_CONTROLLER2 6 60 | #define LEV_CONTROLLER3 7 61 | #define LEV_INSTRUCTOR1 8 62 | #define LEV_INSTRUCTOR2 9 63 | #define LEV_INSTRUCTOR3 10 64 | #define LEV_SUPERVISOR 11 65 | #define LEV_ADMINISTRATOR 12 66 | #define LEV_MAX 12 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /src/interface.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifdef WIN32 4 | #include 5 | #else 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #endif 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "interface.h" 21 | #include "global.h" 22 | #include "support.h" 23 | #include "user.h" 24 | 25 | const char *killreasons[]= 26 | { 27 | "", 28 | "closed on command", 29 | "flooding", 30 | "initial timeout", 31 | "socket stalled", 32 | "connection closed", 33 | "write error", 34 | "killed on command", 35 | "protocol revision error" 36 | }; 37 | tcpinterface::tcpinterface(int port, char *code, char *descr) 38 | { 39 | int on=1; 40 | struct sockaddr_in sa; 41 | description=strdup(descr); 42 | rootallow=NULL, floodlimit=-1, outbuflimit=-1; 43 | makevars(code); 44 | memset(&sa,0,sizeof(sa)); 45 | sa.sin_family=AF_INET; 46 | sa.sin_port=htons(port); 47 | sa.sin_addr.s_addr=htonl(INADDR_ANY); 48 | sock=socket(AF_INET,SOCK_STREAM,0); 49 | if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&on,sizeof(on))<0) 50 | { 51 | dolog(L_ERR,"setsockopt error on port %d",port); 52 | perror("setsockopt"); 53 | exit(1); 54 | } 55 | if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&on,sizeof(on))<0) 56 | { 57 | dolog(L_ERR,"Could not set TCP_NODELAY on port %d",port); 58 | } 59 | if (bind(sock,(struct sockaddr *)&sa, sizeof(sa))<0) 60 | { 61 | dolog(L_CRIT,"Bind error on port %d",port); 62 | perror("bind"); 63 | exit(1); 64 | } 65 | if (listen(sock,5)<0) 66 | { 67 | perror("listen"); 68 | exit(1); 69 | } 70 | rootuser=NULL, prompt=NULL, rootgs=NULL; 71 | feedstrategy=0, prevchecks=0; 72 | dolog(L_INFO,"Booting port %d (%s)",port,description); 73 | } 74 | tcpinterface::~tcpinterface() 75 | { 76 | if (prompt) free(prompt); 77 | if (description) free(description); 78 | CLOSESOCKET(sock); 79 | } 80 | void tcpinterface::makevars(char *code) 81 | { 82 | char varname[100]; 83 | sprintf(varname,"interface.%s.current", code); 84 | varcurrent=manager->addvar(varname, ATT_INT); 85 | sprintf(varname,"interface.%s.total", code); 86 | vartotal=manager->addvar(varname, ATT_INT); 87 | sprintf(varname,"interface.%s.peak", code); 88 | varpeak=manager->addvar(varname, ATT_INT); 89 | 90 | 91 | sprintf(varname,"interface.%s.command", code); 92 | varclosed[1]=manager->addvar(varname, ATT_INT); 93 | sprintf(varname,"interface.%s.flood", code); 94 | varclosed[2]=manager->addvar(varname, ATT_INT); 95 | sprintf(varname,"interface.%s.initialtimeout", code); 96 | varclosed[3]=manager->addvar(varname, ATT_INT); 97 | sprintf(varname,"interface.%s.stalled", code); 98 | varclosed[4]=manager->addvar(varname, ATT_INT); 99 | sprintf(varname,"interface.%s.closed", code); 100 | varclosed[5]=manager->addvar(varname, ATT_INT); 101 | sprintf(varname,"interface.%s.writeerr", code); 102 | varclosed[6]=manager->addvar(varname, ATT_INT); 103 | sprintf(varname,"interface.%s.killed", code); 104 | varclosed[7]=manager->addvar(varname, ATT_INT); 105 | sprintf(varname,"interface.%s.protocol", code); 106 | varclosed[8]=manager->addvar(varname, ATT_INT); 107 | } 108 | void tcpinterface::addguard(absuser *who) 109 | { 110 | guardstruct *temp=(guardstruct *)malloc(sizeof(guardstruct)); 111 | temp->next=rootgs, temp->prev=NULL; 112 | if (temp->next) temp->next->prev=temp; 113 | temp->host=strdup(who->peer), temp->port=who->port; 114 | rootgs=temp, temp->prevtry=mtime(); 115 | } 116 | void tcpinterface::setflood(int limit) 117 | { 118 | floodlimit=limit; 119 | } 120 | void tcpinterface::setfeedstrategy(int strat) 121 | { 122 | feedstrategy=strat; 123 | } 124 | void tcpinterface::setoutbuflimit(int limit) 125 | { 126 | outbuflimit=limit; 127 | } 128 | void tcpinterface::allow(char *name) 129 | { 130 | struct hostent *hent=gethostbyname(name); 131 | if (!hent) 132 | { 133 | dolog(L_ERR,"Server %s unknown in allowfrom", name); 134 | return; 135 | } 136 | allowstruct *a=new allowstruct(); 137 | a->next=rootallow; 138 | rootallow=a; 139 | memcpy(a->ip, (char *)hent->h_addr, 4); 140 | } 141 | int tcpinterface::calcmasks(fd_set *rmask, fd_set *wmask) 142 | { 143 | absuser *temp; 144 | int max=sock; 145 | FD_SET(sock,rmask); 146 | for (temp=rootuser;temp;temp=temp->next) if (!temp->killflag) 147 | { 148 | if (temp->fd>max) max=temp->fd; 149 | temp->setmasks(rmask, wmask); 150 | } 151 | return max; 152 | } 153 | void tcpinterface::newuser(void) 154 | { 155 | struct sockaddr_in saddr; 156 | allowstruct *temp; 157 | char buf[1000]; 158 | #ifdef WIN32 159 | int len=sizeof(saddr); 160 | #else 161 | socklen_t len=sizeof(saddr); 162 | #endif 163 | int ok=0; 164 | int fd=accept(sock,(struct sockaddr *) &saddr, &len); 165 | if (fd<0) return; 166 | for (temp=rootallow;temp;temp=temp->next) 167 | if ((*(int*)temp->ip)==saddr.sin_addr.s_addr) 168 | { 169 | ok=1; 170 | break; 171 | } 172 | #ifdef WIN32 173 | strcpy(buf, inet_ntoa(saddr.sin_addr)); 174 | #else 175 | findhostname(saddr.sin_addr.s_addr, buf); 176 | #endif 177 | if (ok==0&&rootallow) 178 | { 179 | char *message="#You are not allowed on this port.\r\n"; 180 | WRITESOCK(fd,message,strlen(message)); 181 | dolog(L_INFO,"Connection rejected from %s",buf); 182 | CLOSESOCKET(fd); 183 | return; 184 | } 185 | dolog(L_INFO,"Connection accepted from %s on %s", buf, description); 186 | int off=1; 187 | if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&off, 188 | sizeof(off))<0) 189 | { 190 | dolog(L_ERR,"Could not set off TCP_NODELAY"); 191 | } 192 | #ifdef WIN32 193 | unsigned long io=1; 194 | ioctlsocket(sock,FIONBIO,&io); 195 | #endif 196 | newuser(fd, buf, ntohs(saddr.sin_port), 0); 197 | } 198 | void tcpinterface::newuser(int fd, char *peername, int portnum, int g) 199 | { 200 | /* insertuser(new user(fd, peername,portnum)); */ 201 | } 202 | void tcpinterface::insertuser(absuser *u) 203 | { 204 | u->next=rootuser; 205 | if (rootuser) rootuser->prev=u; 206 | rootuser=u; 207 | if (prompt) 208 | { 209 | u->setprompt(prompt); 210 | u->printprompt(); 211 | } 212 | manager->incvar(vartotal); 213 | manager->incvar(varcurrent); 214 | int peak=manager->getvar(varpeak)->value.number; 215 | int now=manager->getvar(varcurrent)->value.number; 216 | if (now>peak) manager->setvar(varpeak, now); 217 | 218 | } 219 | void tcpinterface::removeuser(absuser *u) 220 | { 221 | if (u->next) u->next->prev=u->prev; 222 | if (u->prev) u->prev->next=u->next; else rootuser=u->next; 223 | if (varcurrent!=-1) 224 | manager->decvar(varcurrent); 225 | delete u; 226 | } 227 | int tcpinterface::run() 228 | { 229 | int busy=0; 230 | time_t now=mtime(); 231 | if (FD_ISSET(sock,rmask)) newuser(); 232 | absuser *temp=rootuser; 233 | while (temp) 234 | { 235 | absuser *next=temp->next; 236 | if (temp->killflag==0) 237 | { 238 | if (FD_ISSET(temp->fd,wmask)) temp->output(); 239 | if (FD_ISSET(temp->fd,rmask)) temp->input(); 240 | if (temp->run()) busy=1; 241 | } else removeuser(temp); 242 | temp=next; 243 | } 244 | if (prevchecks!=mtime()) dochecks(); 245 | return busy; 246 | } 247 | void tcpinterface::dochecks() 248 | { 249 | absuser *temp; 250 | time_t now=mtime(); 251 | prevchecks=now; 252 | for (temp=rootuser;temp;temp=temp->next) if (!temp->killflag) 253 | { 254 | if (temp->timeout) 255 | temp->kill(KILL_DATATIMEOUT); 256 | if (feedstrategy&(now-temp->prevfeedcheck>=USERFEEDCHECK)) 257 | temp->calcfeed(); 258 | if (now-temp->lastping>=USERPINGTIMEOUT) 259 | { 260 | temp->sendping(); 261 | temp->lastping=now; 262 | } else if (now-temp->lastactive>=USERTIMEOUT) 263 | { 264 | temp->uprintf("# Timeout\r\n"); 265 | temp->timeout=1; 266 | } 267 | } 268 | guardstruct *tgs=rootgs; 269 | while (tgs) 270 | { 271 | guardstruct *next=tgs->next; 272 | if ((mtime()-tgs->prevtry)>GUARDRETRY) 273 | { 274 | tgs->prevtry=mtime(); 275 | if (adduser(tgs->host, tgs->port, NULL)!=0) 276 | { 277 | free(tgs->host); 278 | if (tgs->next) tgs->next->prev=tgs->prev; 279 | if (tgs->prev) tgs->prev->next=tgs->next; else rootgs=tgs->next; 280 | free(tgs); 281 | } 282 | } 283 | tgs=next; 284 | } 285 | } 286 | void tcpinterface::setprompt(char *s) 287 | { 288 | absuser *temp; 289 | if (prompt) free(prompt); 290 | prompt=strdup(s); 291 | for (temp=rootuser;temp;temp=temp->next) temp->setprompt(s); 292 | } 293 | int tcpinterface::adduser(char *name, int port, absuser *terminal) 294 | { 295 | int sock; 296 | struct sockaddr_in sin; 297 | struct hostent *hent; 298 | absuser *temp; 299 | for (temp=rootuser;temp;temp=temp->next) 300 | if (temp->port==port&&!STRCASECMP(temp->peer, name)) 301 | { 302 | if (terminal) 303 | terminal->uprintf("Already connected to %s\r\n",name); 304 | return -1; 305 | } 306 | hent=gethostbyname(name); 307 | if (!hent) 308 | { 309 | if (terminal) 310 | terminal->uprintf("Unknown hostname: %s\r\n",name); 311 | return 0; 312 | } 313 | sock=socket(AF_INET,SOCK_STREAM,0); 314 | if (sock<0) return 0; 315 | memcpy((char*)&sin.sin_addr, (char*)hent->h_addr, hent->h_length); 316 | sin.sin_port=htons(port), sin.sin_family=AF_INET; 317 | if (terminal) 318 | terminal->uprintf("Connecting to %s port %d.\r\n",name,port); 319 | int error, count=0; 320 | #ifdef WIN32 321 | while ((error=mconnect(sock,(struct sockaddr*)&sin,sizeof(sin),3))==-1&&WSAGetLastError()==WSAEINTR) 322 | #else 323 | while ((error=mconnect(sock,(struct sockaddr*)&sin,sizeof(sin),3))==-1&&errno==EINTR) 324 | #endif 325 | if (++count==3) break; 326 | if (error==-1) 327 | { 328 | #ifdef WIN32 329 | error=WSAGetLastError(); 330 | if (error==WSAEINTR) error=WSAETIMEDOUT; 331 | if (terminal) 332 | terminal->uprintf("Could not connect to server: %s\r\n", 333 | strerror(error)); 334 | #else 335 | error=errno; 336 | if (error==EINTR) error=ETIMEDOUT; 337 | if (terminal) 338 | terminal->uprintf("Could not connect to server: %s\r\n", 339 | strerror(error)); 340 | #endif 341 | CLOSESOCKET(sock); 342 | return 0; 343 | } 344 | if (terminal) 345 | terminal->uprintf("Connection established.\r\n"); 346 | int off=1; 347 | if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&off, 348 | sizeof(off))<0) 349 | { 350 | dolog(L_ERR,"Could not set off TCP_NODELAY"); 351 | } 352 | #ifdef WIN32 353 | unsigned long io=1; 354 | ioctlsocket(sock,FIONBIO,&io); 355 | #endif 356 | newuser(sock, name, port, 1); 357 | return 1; 358 | } 359 | void tcpinterface::delguard() 360 | { 361 | guardstruct *gs=rootgs; 362 | while (gs) 363 | { 364 | guardstruct *next=gs->next; 365 | free(gs->host); 366 | free(gs); 367 | gs=next; 368 | } 369 | rootgs=NULL; 370 | } 371 | -------------------------------------------------------------------------------- /src/interface.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "user.h" 5 | #include "process.h" 6 | 7 | #ifndef INTERFACEHH 8 | #define INTERFACEHH 9 | 10 | struct allowstruct 11 | { 12 | allowstruct *next; 13 | char ip[4]; 14 | }; 15 | struct guardstruct 16 | { 17 | guardstruct *next, *prev; 18 | time_t prevtry; 19 | char *host; 20 | int port; 21 | }; 22 | 23 | extern const char *killreasons[]; 24 | 25 | #define KILL_NONE 0 26 | #define KILL_COMMAND 1 27 | #define KILL_FLOOD 2 28 | #define KILL_INITTIMEOUT 3 29 | #define KILL_DATATIMEOUT 4 30 | #define KILL_CLOSED 5 31 | #define KILL_WRITEERR 6 32 | #define KILL_KILL 7 33 | #define KILL_PROTOCOL 8 34 | 35 | #define FEED_IN 1 36 | #define FEED_OUT 2 37 | #define FEED_BOTH 3 38 | 39 | class user; 40 | class tcpinterface : public process 41 | { 42 | protected: 43 | char *description; 44 | int sock; 45 | int varcurrent, vartotal, varpeak; 46 | int varclosed[9]; 47 | int feedstrategy; 48 | int floodlimit, outbuflimit; 49 | time_t prevchecks; 50 | allowstruct *rootallow; 51 | char *prompt; 52 | 53 | void makevars(char *); 54 | void addguard(absuser *); 55 | virtual int calcmasks(fd_set *, fd_set *); 56 | void newuser(void); 57 | virtual void newuser(int , char *, int, int); 58 | void removeuser(absuser *); 59 | void insertuser(absuser *); 60 | void dochecks(); 61 | 62 | public: 63 | absuser *rootuser; 64 | guardstruct *rootgs; 65 | tcpinterface(int, char *, char *); 66 | ~tcpinterface(); 67 | void setprompt(char *); 68 | void setflood(int); 69 | void setfeedstrategy(int); 70 | void setoutbuflimit(int); 71 | virtual int run(); 72 | int adduser(char *, int, absuser *); 73 | void allow(char *); 74 | void delguard(); 75 | friend class absuser; 76 | }; 77 | #endif 78 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #ifndef WIN32 6 | #include 7 | #include 8 | #include 9 | #endif 10 | 11 | #include "support.h" 12 | #include "fsd.h" 13 | #include "fsdpaths.h" 14 | 15 | 16 | void run(char *configfile) 17 | { 18 | fsd fsdserver(configfile); 19 | fsdserver.run(); 20 | } 21 | void dosignals() 22 | { 23 | signal(SIGPIPE, SIG_IGN); 24 | signal(SIGHUP, SIG_IGN); 25 | } 26 | 27 | #ifdef WIN32 28 | int fsdmain() 29 | { 30 | char currentopath[MAX_PATH]; 31 | GetModuleFileName(NULL,currentopath,sizeof(currentopath)); 32 | char currentpath[MAX_PATH]; 33 | char* filename; 34 | GetFullPathName(currentopath,sizeof(currentpath),currentpath,&filename); 35 | *filename=0; 36 | SetCurrentDirectory(currentpath); 37 | char *configfile; 38 | configfile = PATH_FSD_CONF; 39 | WSADATA lpwsaData; 40 | unsigned short Version = MAKEWORD(2, 2); 41 | int rc = WSAStartup(Version, &lpwsaData); 42 | 43 | dosignals(); 44 | run(configfile); 45 | return 0; 46 | } 47 | 48 | 49 | const char* ServerName = "FSD"; 50 | const char* ServerDesc = "FSD Server"; 51 | 52 | SERVICE_STATUS ServiceStatus; 53 | SERVICE_STATUS_HANDLE ServiceStatusHandle; 54 | DWORD IdThread; 55 | HANDLE hThread; 56 | 57 | VOID WINAPI ServiceCtrlHandler (DWORD Opcode) 58 | { 59 | switch(Opcode) 60 | { 61 | case SERVICE_CONTROL_PAUSE: 62 | ServiceStatus.dwCurrentState = SERVICE_PAUSED; 63 | break; 64 | case SERVICE_CONTROL_CONTINUE: 65 | ServiceStatus.dwCurrentState = SERVICE_RUNNING; 66 | break; 67 | case SERVICE_CONTROL_STOP: 68 | ServiceStatus.dwCurrentState = SERVICE_STOPPED; 69 | TerminateThread(hThread,0); 70 | break; 71 | case SERVICE_CONTROL_INTERROGATE: 72 | break; 73 | } 74 | 75 | SetServiceStatus(ServiceStatusHandle,&ServiceStatus); 76 | return; 77 | } 78 | BOOL ServiceInit() 79 | { 80 | hThread = CreateThread(NULL,4000,(LPTHREAD_START_ROUTINE)fsdmain,NULL,0,&IdThread); 81 | return hThread!=NULL; 82 | } 83 | void WINAPI ServiceMain (DWORD argc, LPTSTR *argv) 84 | { 85 | 86 | ServiceStatus.dwServiceType = SERVICE_WIN32; 87 | ServiceStatus.dwCurrentState = SERVICE_START_PENDING; 88 | ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; 89 | ServiceStatus.dwWin32ExitCode = 0; 90 | ServiceStatus.dwServiceSpecificExitCode = 0; 91 | ServiceStatus.dwCheckPoint = 0; 92 | ServiceStatus.dwWaitHint = 0; 93 | 94 | ServiceStatusHandle = RegisterServiceCtrlHandler(ServerName,ServiceCtrlHandler); 95 | 96 | if (ServiceStatusHandle == (SERVICE_STATUS_HANDLE)0) 97 | return; 98 | 99 | if (!ServiceInit()) 100 | { 101 | ServiceStatus.dwCurrentState = SERVICE_STOPPED; 102 | ServiceStatus.dwCheckPoint = 0; 103 | ServiceStatus.dwWaitHint = 0; 104 | ServiceStatus.dwWin32ExitCode = -1; 105 | ServiceStatus.dwServiceSpecificExitCode = 0; 106 | 107 | SetServiceStatus (ServiceStatusHandle, &ServiceStatus); 108 | return; 109 | } 110 | 111 | ServiceStatus.dwCurrentState = SERVICE_RUNNING; 112 | ServiceStatus.dwCheckPoint = 0; 113 | ServiceStatus.dwWaitHint = 0; 114 | 115 | SetServiceStatus(ServiceStatusHandle, &ServiceStatus); 116 | return; 117 | } 118 | 119 | int main(int argc, char* argv[]) 120 | { 121 | //SetPriorityClass(GetCurrentProcess(),HIGH_PRIORITY_CLASS); 122 | if (argc==2) 123 | { 124 | if (stricmp(argv[1],"/INSTALL")==0) 125 | { 126 | SC_HANDLE scm = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS); 127 | if (scm==NULL) 128 | { 129 | printf("Error : Couldnt open Service Manager, make sure you are administrator\n"); 130 | return -1; 131 | } 132 | char str[4096]; 133 | char currentopath[4096]; 134 | GetModuleFileName(NULL,currentopath,sizeof(currentopath)); 135 | sprintf(str,"%s /SERVICE",currentopath); 136 | SC_HANDLE service = CreateService(scm, 137 | ServerName, 138 | ServerDesc, 139 | SERVICE_ALL_ACCESS, 140 | SERVICE_WIN32_OWN_PROCESS, 141 | SERVICE_AUTO_START, 142 | SERVICE_ERROR_SEVERE, 143 | str, 144 | NULL, 145 | NULL, 146 | NULL, 147 | NULL, 148 | NULL); 149 | if (service==NULL) 150 | { 151 | printf("Error : Couldnt create Service, make sure the service doesnt already exist\n"); 152 | CloseServiceHandle(scm); 153 | return -1; 154 | } 155 | if (!StartService(service,NULL,NULL)) 156 | { 157 | printf("Error : Couldnt start Service\n"); 158 | CloseServiceHandle(service); 159 | CloseServiceHandle(scm); 160 | return -1; 161 | } 162 | CloseServiceHandle(service); 163 | CloseServiceHandle(scm); 164 | printf("Info : Service installed\n"); 165 | return 0; 166 | } 167 | else if (stricmp(argv[1],"/UNINSTALL")==0) 168 | { 169 | SC_HANDLE scm = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS); 170 | if (scm==NULL) 171 | { 172 | printf("Error : Couldnt open Service Manager, make sure you are administrator\n"); 173 | return -1; 174 | } 175 | 176 | SC_HANDLE service = OpenService(scm,ServerName,SERVICE_ALL_ACCESS); 177 | if (service==NULL) 178 | { 179 | printf("Error : Couldnt find Service, make sure the service exists\n"); 180 | CloseServiceHandle(scm); 181 | return -1; 182 | } 183 | SERVICE_STATUS ss; 184 | ControlService(service,SERVICE_CONTROL_STOP,&ss); 185 | 186 | if (!DeleteService(service)) 187 | { 188 | printf("Error : Couldnt start Service\n"); 189 | CloseServiceHandle(service); 190 | CloseServiceHandle(scm); 191 | return -1; 192 | } 193 | CloseServiceHandle(service); 194 | CloseServiceHandle(scm); 195 | printf("Info : Service uninstalled\n"); 196 | return 0; 197 | } 198 | else if (stricmp(argv[1],"/RUN")==0) 199 | { 200 | fsdmain(); 201 | return 0; 202 | } 203 | else if (stricmp(argv[1],"/SERVICE")==0) 204 | { 205 | SERVICE_TABLE_ENTRY DispatchTable[] = 206 | { 207 | { (char*)ServerName, ServiceMain}, 208 | { NULL, NULL } 209 | }; 210 | if (!StartServiceCtrlDispatcher( DispatchTable)) 211 | { 212 | return -1; 213 | } 214 | return 0; 215 | } 216 | } 217 | 218 | printf("USAGE : FSD\n"); 219 | printf("\t/INSTALL : Install as Autostart service\n"); 220 | printf("\t/UNINSTALL : Uninstall service, please reboot after\n"); 221 | printf("\t/RUN : Run Interactive\n"); 222 | 223 | return 0; 224 | } 225 | #else 226 | int main(int argc, char **argv) 227 | { 228 | char *configfile; 229 | int c; 230 | int debug; 231 | 232 | configfile = PATH_FSD_CONF; 233 | debug = 0; 234 | 235 | while ((c = getopt(argc, argv, "df:")) != -1) { 236 | switch (c) { 237 | case 'd': 238 | debug++; 239 | break; 240 | case 'f': 241 | configfile = optarg; 242 | break; 243 | default: 244 | fprintf(stdout, "usage: fsd [-d] [-f configfile]\n"); 245 | exit(1); 246 | } 247 | } 248 | 249 | /* Fork/exec into the background */ 250 | if (!debug) { 251 | /*daemon(0, 0);*/ 252 | } 253 | 254 | run(configfile); 255 | } 256 | #endif 257 | -------------------------------------------------------------------------------- /src/manage.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "global.h" 6 | #include "manage.h" 7 | 8 | manage *manager=NULL; 9 | 10 | manage::manage() 11 | { 12 | nvars=0, variables=NULL; 13 | } 14 | manage::~manage() 15 | { 16 | int x; 17 | for (x=0;x=nvars) return NULL; 74 | return variables+num; 75 | } 76 | int manage::getnvars() 77 | { 78 | return nvars; 79 | } 80 | char *manage::sprintvalue(int num, char *buf) 81 | { 82 | if (num>=nvars||variables[num].name==NULL) return NULL; 83 | switch (variables[num].type) 84 | { 85 | case ATT_VARCHAR: sprintf(buf,"\"%s\"",variables[num].value.string); 86 | break; 87 | case ATT_INT : sprintf(buf,"%d",variables[num].value.number); break; 88 | case ATT_DATE : 89 | sprintf(buf,"%s",ctime(&variables[num].value.timeval)); 90 | buf[strlen(buf)-1]='\0'; 91 | break; 92 | } 93 | return buf; 94 | } 95 | int manage::getvarnum(char *name) 96 | { 97 | int x; 98 | for (x=0;x 2 | 3 | #ifndef MANAGEHH 4 | #define MANAGEHH 5 | 6 | #define ATT_INT 1 7 | #define ATT_FLOAT 2 8 | #define ATT_MONEY 3 9 | #define ATT_CHAR 4 10 | #define ATT_VARCHAR 5 11 | #define ATT_DATE 6 12 | #define ATT_BLOB 7 13 | #define ATT_NULL 10 14 | 15 | struct managevar 16 | { 17 | int type; 18 | char *name; 19 | union 20 | { 21 | int number; 22 | char *string; 23 | time_t timeval; 24 | } value; 25 | }; 26 | class manage 27 | { 28 | int nvars; 29 | managevar *variables; 30 | public: 31 | manage(); 32 | ~manage(); 33 | int addvar(char *, int); 34 | int getvarnum(char *name); 35 | void incvar(int); 36 | void setvar(int, int); 37 | void setvar(int, char *); 38 | void setvar(int, time_t); 39 | void decvar(int); 40 | void delvar(int); 41 | managevar *getvar(int); 42 | int getnvars(); 43 | char *sprintvalue(int, char *); 44 | }; 45 | 46 | extern manage *manager; 47 | 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/mm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifdef WIN32 4 | #include 5 | #else 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #endif 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "mm.h" 17 | #include "fsd.h" 18 | #include "wprofile.h" 19 | #include "manage.h" 20 | #include "support.h" 21 | #include "config.h" 22 | #include "global.h" 23 | 24 | mm *metarmanager=NULL; 25 | 26 | mm::mm() 27 | { 28 | prevdownload=0L, ioin=ioout=NULL, newfileready=0; 29 | sock=datasock=datareadsock=-1; 30 | passivemode=1; sockrecvbufferlen=0; 31 | 32 | source=SOURCE_NETWORK, downloading=0, rootq=NULL, ftpemail=NULL; 33 | prevhour=-1, nstations=0, stationlist=NULL, metarhost=metardir=NULL; 34 | configgroup *wgroup=configman->getgroup("weather"); 35 | configentry *wentry=wgroup?wgroup->getentry("source"):(configentry*)NULL; 36 | if (wentry) 37 | { 38 | char *buf=wentry->getdata(); 39 | if (!STRCASECMP(buf,"network")) source=SOURCE_NETWORK; else 40 | if (!STRCASECMP(buf,"file")) source=SOURCE_FILE; else 41 | if (!STRCASECMP(buf,"download")) source=SOURCE_DOWNLOAD; else 42 | dolog(L_ERR,"Unknown METAR source '%s' in config file", buf); 43 | } 44 | wentry=wgroup?wgroup->getentry("server"):(configentry*)NULL; 45 | if (wentry) metarhost=strdup(wentry->getdata()); 46 | wentry=wgroup?wgroup->getentry("dir"):(configentry*)NULL; 47 | if (wentry) metardir=strdup(wentry->getdata()); 48 | wentry=wgroup?wgroup->getentry("ftpmode"):(configentry*)NULL; 49 | if (wentry) 50 | { 51 | char *buf=wentry->getdata(); 52 | if (!STRCASECMP(buf,"passive")) passivemode=1; else 53 | if (!STRCASECMP(buf,"ative")) passivemode=0; 54 | } 55 | 56 | wgroup=configman->getgroup("system"); 57 | wentry=wgroup?wgroup->getentry("email"):(configentry*)NULL; 58 | if (wentry) ftpemail=strdup(wentry->getdata()); 59 | 60 | if (source==SOURCE_DOWNLOAD) 61 | { 62 | if (!metarhost) metarhost=strdup("tgftp.nws.noaa.gov"); 63 | if (!metardir) metardir=strdup("data/observations/metar/cycles/"); 64 | } 65 | 66 | int var=manager->addvar("metar.method", ATT_VARCHAR); 67 | manager->setvar(var, (char *)(source==SOURCE_NETWORK?"network": 68 | source==SOURCE_FILE?"file":"download")); 69 | varprev=manager->addvar("metar.current", ATT_DATE); 70 | vartotal=manager->addvar("metar.requests", ATT_INT); 71 | varstations=manager->addvar("metar.stations", ATT_INT); 72 | manager->setvar(varprev, time(NULL)); 73 | if (source==SOURCE_FILE) buildlist(); 74 | } 75 | mm::~mm() 76 | { 77 | if (ioin) fclose(ioin); 78 | if (ioout) fclose(ioout); 79 | if (stationlist) free(stationlist); 80 | if (metarhost) free(metarhost); 81 | if (metardir) free(metardir); 82 | } 83 | int mm::calcmasks(fd_set *rmask, fd_set *wmask) 84 | { 85 | if (downloading!=1) return 0; 86 | int max=sock; 87 | if (datasock>max) max=datasock; 88 | if (datareadsock>max) max=datareadsock; 89 | FD_SET(sock, rmask); 90 | if (datareadsock!=-1) FD_SET(datareadsock, rmask); else 91 | FD_SET(datasock, rmask); 92 | return max; 93 | } 94 | void mm::check() 95 | { 96 | time_t now=time(NULL); 97 | struct tm *t=gmtime(&now); 98 | if (prevhour==-1||(prevhour!=t->tm_hour&&(t->tm_hour==12||t->tm_hour==0))) 99 | { 100 | int x; 101 | prevhour=t->tm_hour; 102 | msrand((+t->tm_hour)*t->tm_year*(1+t->tm_mon)); 103 | for (x=0;xtm_min==10&&elapsed>60)||(elapsed>3600)||prevdownload==0) 111 | startdownload(); 112 | } 113 | } 114 | int comparestation(const void *p1, const void *p2) 115 | { 116 | station *s1=(station *)p1, *s2=(station *)p2; 117 | return strcmp(s1->name, s2->name); 118 | } 119 | void mm::buildlist() 120 | { 121 | FILE *io=fopen(METARFILE,"r"); 122 | struct stat statbuf; 123 | off_t offset; 124 | int max=2000; 125 | char line[100], *array[3]; 126 | if (!io) return; 127 | fstat(fileno(io), &statbuf); 128 | metarfiletime=statbuf.st_mtime; 129 | nstations=0; 130 | if (stationlist) free(stationlist); 131 | stationlist=(station*)malloc(sizeof(station)*max); 132 | for (offset=(off_t) 0;fgets(line, 100, io);offset=(off_t)ftell(io)) 133 | { 134 | if (strlen(line)<30) continue; 135 | int count=breakargs(line, array, 3); 136 | if (count<3) continue; 137 | if (strncmp(line," ",5)==0) continue; 138 | char *statname=(strlen(array[0])==4)?array[0]:strlen(array[1])==4? 139 | array[1]:(char*)NULL; 140 | if (!statname) continue; 141 | if (nstations==max) 142 | stationlist=(station*) realloc(stationlist, sizeof(station)* 143 | (max+=1000)); 144 | stationlist[nstations].location=offset; 145 | strupr(statname); 146 | strcpy(stationlist[nstations++].name,statname); 147 | } 148 | stationlist=(station*)realloc(stationlist, sizeof(station)*nstations); 149 | qsort(stationlist, nstations, sizeof(station), comparestation); 150 | manager->setvar(varstations, nstations); 151 | fclose(io); 152 | } 153 | void mm::setupnewfile() 154 | { 155 | newfileready=0; 156 | if (metarsize<100000) 157 | { 158 | unlink(METARFILENEW); 159 | dolog(L_WARNING, "METAR: Size of new METAR file (%d) is too small, dropping.", 160 | metarsize); 161 | return; 162 | } 163 | unlink(METARFILE); 164 | if (rename(METARFILENEW, METARFILE)) 165 | dolog(L_ERR, "METAR: Can't move %s to %s: %s",METARFILENEW, METARFILE, 166 | strerror(errno)); 167 | else dolog(L_INFO,"METAR: Installed new METAR data."); 168 | buildlist(); 169 | manager->setvar(varprev, time(NULL)); 170 | } 171 | int mm::run() 172 | { 173 | if (source==SOURCE_NETWORK) return 0; 174 | if (downloading) dodownload(); 175 | while (rootq) if (doparse(rootq)==0) break; 176 | if (!downloading&&newfileready) setupnewfile(); 177 | check(); 178 | return 0; 179 | } 180 | void mm::stopdownload() 181 | { 182 | if (sockrecvbufferlen) 183 | free(sockrecvbuffer); 184 | sockrecvbufferlen=0; 185 | if (ioin) fclose(ioin); 186 | if (sock!=-1) CLOSESOCKET(sock); 187 | if (datasock!=-1) CLOSESOCKET(datasock); 188 | if (datareadsock!=-1) CLOSESOCKET(datareadsock); 189 | ioin=NULL, downloading=0, sock=datasock=datareadsock=-1; 190 | } 191 | void mm::startdownload() 192 | { 193 | struct sockaddr_in sin, dataadr; 194 | struct hostent *hent; 195 | char *s, cv[100]; 196 | if (downloading) 197 | { 198 | dolog(L_ERR, "METAR: server seems to be still loading"); 199 | return; 200 | } 201 | prevdownload=mtime(); 202 | ioin=fopen(METARFILENEW,"w"); 203 | if (!ioin) 204 | { 205 | dolog(L_ERR, "METAR: Can't open %s.", METARFILENEW); 206 | stopdownload(); 207 | return; 208 | } 209 | hent=gethostbyname(metarhost); 210 | if (!hent) 211 | { 212 | dolog(L_ERR, "METAR: Could not lookup METAR host name %s.", metarhost); 213 | stopdownload(); 214 | return; 215 | } 216 | sock=socket(AF_INET,SOCK_STREAM,0); 217 | if (sock<0) return; 218 | memcpy((char*)&sin.sin_addr, (char*)hent->h_addr, hent->h_length); 219 | sin.sin_port=htons(21), sin.sin_family=AF_INET; 220 | if (connect(sock,(struct sockaddr*)&sin,sizeof(sin))==-1) 221 | { 222 | dolog(L_ERR, "METAR: Could not connect to ftp port on METAR host"); 223 | stopdownload(); 224 | return; 225 | } 226 | dataadr.sin_family=AF_INET; 227 | dataadr.sin_port=htons(0); 228 | dataadr.sin_addr.s_addr=htonl(INADDR_ANY); 229 | datasock=socket(AF_INET,SOCK_STREAM,0); 230 | bind(datasock, (struct sockaddr *)&dataadr, sizeof(dataadr)); 231 | listen(datasock, 1); 232 | 233 | char data[1000], port[100]; 234 | socklen_t name=sizeof(dataadr); 235 | getsockname(datasock, (struct sockaddr *)&dataadr, &name); 236 | int portnum=ntohs(dataadr.sin_port); 237 | name=sizeof(dataadr); 238 | getsockname(sock, (struct sockaddr *)&dataadr, &name); 239 | unsigned long adr=ntohl((unsigned long)dataadr.sin_addr.s_addr); 240 | 241 | sprintf(port, "%d,%d,%d,%d,%d,%d", (adr>>24)&0xff, (adr>>16)&0xff, 242 | (adr>>8)&0xff, adr&0xff, (portnum>>8)&0xff, portnum&0xff); 243 | 244 | time_t now=time(NULL); 245 | struct tm *tp=gmtime(&now); 246 | if (passivemode) 247 | { 248 | sprintf(data,"USER anonymous\nPASS %s\nCWD %s\nPASV\n", 249 | ftpemail, metardir); 250 | WRITESOCK(sock, data, strlen(data)); 251 | downloading=1, metarsize=0, datareadsock=-1; 252 | } 253 | else 254 | { 255 | sprintf(data,"USER anonymous\nPASS %s\nCWD %s\nPORT %s\nRETR %02dZ.TXT\n", 256 | ftpemail, metardir, port, (tp->tm_hour+21)%24); 257 | WRITESOCK(sock, data, strlen(data)); 258 | dolog(L_INFO,"METAR: Starting download of METAR data"); 259 | downloading=1, metarsize=0, datareadsock=-1; 260 | } 261 | } 262 | void mm::dodownload() 263 | { 264 | if ((mtime()-prevdownload)>MAXMETARDOWNLOADTIME) 265 | { 266 | dolog(L_WARNING, "METAR download interrupted due to timeout"); 267 | stopdownload(); 268 | startdownload(); 269 | return; 270 | } 271 | if (FD_ISSET(sock, rmask)) 272 | { 273 | char buf[4096]; 274 | int bytes=READSOCK(sock, buf, 4096); 275 | /*char* sockrecvbuffer; 276 | int sockrecvbufferlen;*/ 277 | if (bytes<=0) 278 | { 279 | stopdownload(); 280 | newfileready=1; 281 | } 282 | else if (passivemode)//&&(datareadsock==-1)) 283 | { 284 | char* newbuffer = (char*) malloc(bytes+sockrecvbufferlen); 285 | memcpy(newbuffer,sockrecvbuffer,sockrecvbufferlen); 286 | if (sockrecvbufferlen) 287 | free(sockrecvbuffer); 288 | memcpy(&newbuffer[sockrecvbufferlen],buf,bytes); 289 | sockrecvbufferlen+=bytes; 290 | sockrecvbuffer=newbuffer; 291 | char* sentence = newbuffer; 292 | int p = 0; 293 | while (ptm_hour+21)%24); 324 | WRITESOCK(sock, data, strlen(data)); 325 | dolog(L_INFO,"METAR: Starting download of METAR data"); 326 | } 327 | } 328 | } 329 | if (sockrecvbuffer[p]==0xa) 330 | { 331 | sentence = &sockrecvbuffer[p+1]; 332 | } 333 | p++; 334 | } 335 | if ((sentence=sockrecvbuffer+sockrecvbufferlen) 344 | { 345 | if (sockrecvbufferlen) 346 | free(sockrecvbuffer); 347 | sockrecvbufferlen=0; 348 | } 349 | } 350 | } 351 | if (datareadsock==-1) 352 | { 353 | if (!FD_ISSET(datasock, rmask)) return; 354 | datareadsock=accept(datasock, NULL, NULL); 355 | return; 356 | } 357 | if (!FD_ISSET(datareadsock, rmask)) return; 358 | char buf[1024]; 359 | int bytes=READSOCK(datareadsock, buf, 1024); 360 | if (bytes<=0) 361 | { 362 | stopdownload(); 363 | newfileready=1; 364 | } else 365 | { 366 | fwrite(buf, 1, bytes, ioin); 367 | metarsize+=bytes; 368 | } 369 | } 370 | void mm::checkmetarfile() 371 | { 372 | struct stat sb; 373 | if (stat(METARFILE, &sb)==-1) return; 374 | if (sb.st_mtime!=metarfiletime) buildlist(); 375 | } 376 | int mm::doparse(mmq *q) 377 | { 378 | station key; 379 | checkmetarfile(); 380 | strncpy(key.name, q->metarid, 4); 381 | key.name[4]='\0'; 382 | strupr(key.name); 383 | station *location=(station *) bsearch(&key, stationlist, nstations, 384 | sizeof(station), comparestation); 385 | if (!location) 386 | { 387 | serverinterface->sendnowx(q->destination, q->fd, q->metarid); 388 | delq(q); 389 | return 1; 390 | } 391 | ioout=fopen(METARFILE, "r"); 392 | if (!ioout) return 0; 393 | fseek(ioout, location->location, SEEK_SET); 394 | char line[500]="", piece[100]; 395 | int first=1; 396 | do 397 | { 398 | if (!getline(piece)) break; 399 | int addition=strncmp(piece, " ", 5)==0; 400 | if (addition&&first) break; 401 | if (!addition&&!first) break; 402 | first=0; 403 | strcat(line, piece+(addition?4:0)); 404 | } while (1); 405 | fclose(ioout); 406 | ioout=NULL; 407 | if (q->parsed) 408 | { 409 | char *array[100]; 410 | int count=breakargs(line, array, 100); 411 | wprofile pr(location->name, 0, NULL); 412 | pr.parsemetar(array,count); 413 | serverinterface->sendweather(q->destination, q->fd, &pr); 414 | } else 415 | { 416 | serverinterface->sendmetar(q->destination, q->fd, 417 | location->name, line); 418 | } 419 | manager->incvar(vartotal); 420 | delq(q); 421 | return 1; 422 | } 423 | 424 | 425 | int mm::getline(char *line) 426 | { 427 | if (!fgets(line, 100, ioout)) 428 | return 0; 429 | prepareline(line); 430 | return 1; 431 | } 432 | void mm::prepareline(char *line) 433 | { 434 | int pos=strlen(line)-1; 435 | while (line[pos]=='\n'||line[pos]=='\r'||line[pos]=='=') 436 | line[pos--]='\0'; 437 | } 438 | server *mm::requestmetar(char *client, char *metar, int parsed, int fd) 439 | { 440 | if (source==SOURCE_NETWORK) 441 | { 442 | server *temp, *best=NULL; 443 | int hops=-1; 444 | for (temp=rootserver;temp;temp=temp->next) 445 | if (temp!=myserver&&(temp->flags&SERVER_METAR)) 446 | if (hops==-1||(temp->hopshops!=-1)) 447 | best=temp, hops=temp->hops; 448 | if (!best) return NULL; 449 | serverinterface->sendreqmetar(client, metar, fd, parsed, best); 450 | return best; 451 | } else addq(client, metar, parsed, fd); 452 | return myserver; 453 | } 454 | void mm::addq(char *dest, char *metar, int parsed, int fd) 455 | { 456 | wprofile *prof=getwprofile(metar); 457 | if (prof&&prof->active) 458 | { 459 | if (parsed) serverinterface->sendweather(dest, fd, prof); else 460 | serverinterface->sendmetar(dest, fd, metar, prof->rawcode); 461 | return; 462 | } 463 | mmq *temp=new mmq; 464 | temp->destination=strdup(dest), temp->metarid=strdup(metar); 465 | temp->fd=fd, temp->next=rootq, temp->prev=NULL; 466 | temp->parsed=parsed; 467 | if (temp->next) temp->next->prev=temp; 468 | rootq=temp; 469 | } 470 | void mm::delq(mmq *p) 471 | { 472 | free(p->destination); 473 | free(p->metarid); 474 | if (p->next) p->next->prev=p->prev; 475 | if (p->prev) p->prev->next=p->next; else rootq=p->next; 476 | delete p; 477 | } 478 | int mm::getvariation(int num, int min, int max) 479 | { 480 | int val=variation[num], range=max-min+1; 481 | val=(abs(val)%range)+min; 482 | return val; 483 | } 484 | -------------------------------------------------------------------------------- /src/mm.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifndef WIN32 4 | #include 5 | #include 6 | #include 7 | #endif 8 | 9 | #include "server.h" 10 | #include "process.h" 11 | 12 | #ifndef mmhh 13 | #define mmhh 14 | 15 | #define SOURCE_NETWORK 0 16 | #define SOURCE_FILE 1 17 | #define SOURCE_DOWNLOAD 2 18 | 19 | #define VAR_AMOUNT 10 20 | 21 | struct station 22 | { 23 | char name[5]; 24 | off_t location; 25 | }; 26 | struct mmq 27 | { 28 | mmq *next, *prev; 29 | char *destination, *metarid; 30 | int fd, parsed; 31 | }; 32 | 33 | class mm : public process 34 | { 35 | FILE *ioin, *ioout; 36 | mmq *rootq; 37 | time_t prevdownload, metarfiletime; 38 | int nstations; 39 | station *stationlist; 40 | int prevhour; 41 | int sock, datasock, datareadsock, newfileready, metarsize; 42 | char* sockrecvbuffer; 43 | int sockrecvbufferlen; 44 | int varprev, vartotal, varstations; 45 | int variation[VAR_AMOUNT]; 46 | char *metarhost, *metardir, *ftpemail; 47 | int passivemode; 48 | void setupnewfile(); 49 | void buildlist(); 50 | int doparse(mmq *); 51 | void dodownload(); 52 | void prepareline(char *); 53 | int getline(char *); 54 | void check(); 55 | void checkmetarfile(); 56 | void addq(char *, char *, int, int); 57 | void delq(mmq *); 58 | virtual int calcmasks(fd_set *, fd_set *); 59 | 60 | public: 61 | void startdownload(); 62 | void stopdownload(); 63 | int downloading, source; 64 | mm(); 65 | ~mm(); 66 | virtual int run(); 67 | server *requestmetar(char *, char *, int, int); 68 | int getvariation(int, int, int); 69 | }; 70 | 71 | extern mm *metarmanager; 72 | #endif 73 | -------------------------------------------------------------------------------- /src/process.cpp: -------------------------------------------------------------------------------- 1 | #ifndef WIN32 2 | #include 3 | #endif 4 | #include 5 | #include 6 | #include 7 | 8 | #include "process.h" 9 | #include "global.h" 10 | #include "server.h" 11 | 12 | int process::calcmasks(fd_set *, fd_set *) { return 0; } 13 | int process::run() { return 0; } 14 | process::process() 15 | { 16 | next=prev=NULL; 17 | } 18 | 19 | 20 | pman::pman() 21 | { 22 | rootprocess=NULL; 23 | busy=0; 24 | } 25 | void pman::registerprocess(process *what) 26 | { 27 | what->next=rootprocess; 28 | if (what->next) what->next->prev=what; 29 | rootprocess=what; 30 | } 31 | void pman::run() 32 | { 33 | int max=0; 34 | process *temp; 35 | struct timeval timeout; 36 | fd_set rmask, wmask; 37 | FD_ZERO(&rmask); FD_ZERO(&wmask); 38 | for (temp=rootprocess;temp;temp=temp->next) 39 | { 40 | int num=temp->calcmasks(&rmask, &wmask); 41 | if (num>max) max=num; 42 | } 43 | timeout.tv_usec=0, timeout.tv_sec=(busy?0:1); 44 | if (select(max+1, &rmask, &wmask, NULL, &timeout)<=0) 45 | { 46 | FD_ZERO(&rmask); FD_ZERO(&wmask); 47 | } 48 | busy=0; 49 | for (temp=rootprocess;temp;temp=temp->next) 50 | { 51 | temp->rmask=&rmask, temp->wmask=&wmask; 52 | if (temp->run()) busy=1; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/process.h: -------------------------------------------------------------------------------- 1 | #ifndef WIN32 2 | #include 3 | #include 4 | #include 5 | #else 6 | #include 7 | #endif 8 | 9 | #ifndef PROCESSHH 10 | #define PROCESSHH 11 | 12 | class pman; 13 | class process 14 | { 15 | process *next, *prev; 16 | protected: 17 | fd_set *rmask, *wmask; 18 | virtual int calcmasks(fd_set *, fd_set *); 19 | virtual int run(); 20 | public: 21 | process(); 22 | friend class pman; 23 | }; 24 | 25 | class pman 26 | { 27 | int busy; 28 | process *rootprocess; 29 | public: 30 | pman(); 31 | void registerprocess(process *); 32 | void run(); 33 | }; 34 | #endif 35 | -------------------------------------------------------------------------------- /src/protocol.h: -------------------------------------------------------------------------------- 1 | #ifndef PROTOCOLH 2 | #define PROTOCOLH 3 | 4 | #define CMD_NOTIFY 0 5 | #define CMD_REQMETAR 1 6 | #define CMD_PING 2 7 | #define CMD_PONG 3 8 | #define CMD_SYNC 4 9 | #define CMD_LINKDOWN 5 10 | #define CMD_NOWX 6 11 | #define CMD_ADDCLIENT 7 12 | #define CMD_RMCLIENT 8 13 | #define CMD_PLAN 9 14 | #define CMD_PD 10 15 | #define CMD_AD 11 16 | #define CMD_CERT 12 17 | #define CMD_MULTIC 13 18 | #define CMD_WEATHER 14 19 | #define CMD_METAR 15 20 | #define CMD_ADDWPROF 16 21 | #define CMD_DELWPROF 17 22 | #define CMD_KILL 18 23 | #define CMD_RESET 19 24 | 25 | #define CL_ADDATC 0 26 | #define CL_RMATC 1 27 | #define CL_ADDPILOT 2 28 | #define CL_RMPILOT 3 29 | #define CL_REQHANDOFF 4 30 | #define CL_MESSAGE 5 31 | #define CL_REQWEATHER 6 32 | #define CL_PILOTPOS 7 33 | #define CL_ATCPOS 8 34 | #define CL_PING 9 35 | #define CL_PONG 10 36 | #define CL_ACHANDOFF 11 37 | #define CL_PLAN 12 38 | #define CL_SB 13 39 | #define CL_PC 14 40 | #define CL_WEATHER 15 41 | #define CL_CLOUDDATA 16 42 | #define CL_WINDDATA 17 43 | #define CL_TEMPDATA 18 44 | #define CL_REQCOM 19 45 | #define CL_REPCOM 20 46 | #define CL_REQACARS 21 47 | #define CL_REPACARS 22 48 | #define CL_ERROR 23 49 | #define CL_CQ 24 50 | #define CL_CR 25 51 | #define CL_KILL 26 52 | #define CL_WDELTA 27 53 | 54 | #define CL_MAX 27 55 | 56 | #define CERT_ADD 0 57 | #define CERT_DELETE 1 58 | #define CERT_MODIFY 2 59 | 60 | #define ERR_OK 0 /* No error */ 61 | #define ERR_CSINUSE 1 /* Callsign in use */ 62 | #define ERR_CSINVALID 2 /* Callsign invalid */ 63 | #define ERR_REGISTERED 3 /* Already registered */ 64 | #define ERR_SYNTAX 4 /* Syntax error */ 65 | #define ERR_SRCINVALID 5 /* Invalid source in packet */ 66 | #define ERR_CIDINVALID 6 /* Invalid CID/password */ 67 | #define ERR_NOSUCHCS 7 /* No such callsign */ 68 | #define ERR_NOFP 8 /* No flightplan */ 69 | #define ERR_NOWEATHER 9 /* No such weather profile*/ 70 | #define ERR_REVISION 10 /* Invalid protocol revision */ 71 | #define ERR_LEVEL 11 /* Requested level too high */ 72 | #define ERR_SERVFULL 12 /* No more clients */ 73 | #define ERR_CSSUSPEND 13 /* CID/PID suspended */ 74 | 75 | extern const char *cmdnames[]; 76 | extern const char *clcmdnames[]; 77 | extern const char *errstr[]; 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /src/server.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "server.h" 6 | #include "support.h" 7 | #include "client.h" 8 | #include "servuser.h" 9 | #include "fsd.h" 10 | #include "global.h" 11 | 12 | server *rootserver=NULL; 13 | server *myserver=NULL; 14 | server::server(char *i, char *n, char *e, char *h, char *v, int fl, char *l) 15 | { 16 | next=rootserver, prev=NULL; 17 | if (next) next->prev=this; 18 | rootserver=this; 19 | ident=strdup(i), name=strdup(n), email=strdup(e), hostname=strdup(h); 20 | version=strdup(v), flags=fl, packetdrops=0, location=strdup(l); 21 | path=NULL, hops=-1, pcount=-1, lag=-1; 22 | alive=mtime(); 23 | } 24 | void server::configure(char *n, char *e, char *h, char *v, char *l) 25 | { 26 | if (name) free(name); 27 | if (email) free(email); 28 | if (hostname) free(hostname); 29 | if (version) free(version); 30 | if (location) free(location); 31 | name=strdup(n), email=strdup(e), hostname=strdup(h), version=strdup(v); 32 | location=strdup(l); 33 | } 34 | server::~server() 35 | { 36 | client *tempclient=rootclient; 37 | dolog(L_ERR,"Dropping server %s(%s)", ident, name); 38 | free(ident); free(name); free(email); free(hostname); free(version); 39 | if (next) next->prev=prev; 40 | if (prev) prev->next=next; else rootserver=next; 41 | while (tempclient) 42 | { 43 | client *next=tempclient->next; 44 | if (tempclient->location==this) 45 | delete tempclient; 46 | tempclient=next; 47 | } 48 | absuser *t; 49 | for (t=serverinterface->rootuser;t;t=t->next) 50 | if (((servuser*)t)->thisserver==this) 51 | ((servuser*)t)->thisserver=NULL; 52 | } 53 | server *getserver(char *ident) 54 | { 55 | server *temp; 56 | for (temp=rootserver;temp;temp=temp->next) 57 | if (!STRCASECMP(temp->ident,ident)) return temp; 58 | return NULL; 59 | } 60 | void server::setpath(absuser *who, int h) 61 | { 62 | path=who, hops=h; 63 | if (path==NULL) pcount=-1; 64 | } 65 | void server::setalive() 66 | { 67 | alive=mtime(); 68 | } 69 | void server::receivepong(char *data) 70 | { 71 | int fd; 72 | time_t t; 73 | if (sscanf(data,"%d %ld", &fd, &t)!=2) return; 74 | lag=mtime()-t; 75 | } 76 | void clearserverchecks() 77 | { 78 | server *temp; 79 | for (temp=rootserver;temp;temp=temp->next) temp->check=0; 80 | } 81 | -------------------------------------------------------------------------------- /src/server.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "interface.h" 4 | 5 | #ifndef SERVERHH 6 | #define SERVERHH 7 | 8 | #define SERVER_METAR 1 9 | #define SERVER_SILENT 2 10 | 11 | class server 12 | { 13 | public: 14 | int pcount, hops, check, lag, flags; 15 | int packetdrops; 16 | time_t alive; 17 | char *name; 18 | char *email; 19 | char *ident; 20 | char *hostname; 21 | char *version; 22 | char *location; 23 | absuser *path; 24 | server *next, *prev; 25 | server(char *, char *, char *, char *, char *, int, char *); 26 | void configure(char *, char *, char *, char *, char *); 27 | /* void sendping();*/ 28 | ~server(); 29 | void setpath(absuser *, int); 30 | void setalive(); 31 | void receivepong(char *); 32 | }; 33 | server *getserver(char *); 34 | void clearserverchecks(); 35 | extern server *rootserver; 36 | extern server *myserver; 37 | #endif 38 | -------------------------------------------------------------------------------- /src/servinterface.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifndef WIN32 4 | #include 5 | #include 6 | #endif 7 | #include 8 | #include 9 | 10 | #include "interface.h" 11 | #include "servinterface.h" 12 | #include "global.h" 13 | #include "support.h" 14 | #include "server.h" 15 | #include "protocol.h" 16 | #include "client.h" 17 | #include "servuser.h" 18 | #include "fsd.h" 19 | 20 | /* The serverinterface class */ 21 | 22 | servinterface::servinterface(int port, char *code, char *d): 23 | tcpinterface(port, code, d) 24 | { 25 | packetcount=0; 26 | varmchandled=manager->addvar("protocol.multicast.handled", ATT_INT); 27 | varmcdrops=manager->addvar("protocol.multicast.dropped", ATT_INT); 28 | varuchandled=manager->addvar("protocol.unicast.handled", ATT_INT); 29 | varucoverrun=manager->addvar("protocol.unicast.overruns", ATT_INT); 30 | varfailed=manager->addvar("protocol.errors.invalidcommand", ATT_INT); 31 | varbounce=manager->addvar("protocol.errors.bounce", ATT_INT); 32 | varshape=manager->addvar("protocol.errors.shape", ATT_INT); 33 | varinterr=manager->addvar("protocol.errors.integer", ATT_INT); 34 | serverident=myserver->ident; 35 | } 36 | int servinterface::run() 37 | { 38 | absuser *temp; 39 | time_t now=mtime(); 40 | int busy=tcpinterface::run(); 41 | if ((now-lastsync)>SYNCTIMEOUT) sendsync(); 42 | for (temp=rootuser;temp;temp=temp->next) 43 | { 44 | servuser *stemp=(servuser *)temp; 45 | if (!stemp->clientok&&(now-stemp->startuptime)>SERVERMAXTOOK) 46 | stemp->kill(KILL_INITTIMEOUT); 47 | } 48 | return busy; 49 | } 50 | void servinterface::newuser(int fd, char *peer, int portnum, int g) 51 | { 52 | insertuser(new servuser(fd,this,peer,portnum,g)); 53 | } 54 | 55 | void servinterface::incpacketcount() 56 | { 57 | packetcount++; 58 | /* Now check if the integer looped */ 59 | if (packetcount<0||packetcount>2000000000) 60 | sendreset(); 61 | } 62 | /* Send a SYNC packet */ 63 | void servinterface::sendsync() 64 | { 65 | sendpacket(NULL, NULL, CMD_SYNC, "*", serverident, packetcount, 0, 0, ""); 66 | lastsync=mtime(); 67 | incpacketcount(); 68 | } 69 | /* Send a NOTIFY packet to a destination */ 70 | void servinterface::sendservernotify(char *dest, server *subject, 71 | absuser *towho) 72 | { 73 | char buf[1000]; 74 | sprintf(buf,"%d:%s:%s:%s:%s:%s:%d:%s", subject==myserver?0:1, subject->ident, 75 | subject->name, subject->email, subject->hostname, subject->version, 76 | subject->flags, subject->location); 77 | sendpacket(NULL, towho, CMD_NOTIFY, dest, serverident, packetcount, 0, 78 | 1, buf); 79 | incpacketcount(); 80 | } 81 | /* Send a REQMETAR packet */ 82 | void servinterface::sendreqmetar(char *client, char *metar, int fd, 83 | int parsed, server *dest) 84 | { 85 | char buf[1000]; 86 | sprintf(buf,"%s:%s:%d:%d",client,metar,parsed,fd); 87 | sendpacket(NULL, NULL, CMD_REQMETAR, dest->ident, serverident, 88 | packetcount, 0, 0, buf); 89 | incpacketcount(); 90 | } 91 | /* Send a LINKDOWN packet to a destination */ 92 | void servinterface::sendlinkdown(char *data) 93 | { 94 | sendpacket(NULL, NULL, CMD_LINKDOWN, "*", serverident, packetcount, 95 | 0, 1, data); 96 | incpacketcount(); 97 | } 98 | /* Send a PONG packet to a destination */ 99 | void servinterface::sendpong(char *dest, char *data) 100 | { 101 | sendpacket(NULL, NULL, CMD_PONG, dest, serverident, packetcount, 0, 0, data); 102 | incpacketcount(); 103 | } 104 | /* Broadcast a PING packet to a destination*/ 105 | void servinterface::sendping(char *dest, char *data) 106 | { 107 | sendpacket(NULL, NULL, CMD_PING, dest, serverident, packetcount, 0, 0, data); 108 | incpacketcount(); 109 | } 110 | /* Send an ADDCLIENT packet to a destination */ 111 | void servinterface::sendaddclient(char *dest, client *who, absuser *direction, 112 | absuser *source, int feed) 113 | { 114 | char buf[1000]; 115 | sprintf(buf,"%s:%s:%s:%d:%d:%s:%s:%d",who->cid,who->location->ident, 116 | who->callsign,who->type, who->rating, who->protocol, who->realname, 117 | who->simtype); 118 | sendpacket(NULL, direction, CMD_ADDCLIENT, dest, serverident, 119 | packetcount, 0, 0, buf); 120 | incpacketcount(); 121 | /* This command is important for clients as well, so if it's not a feed, 122 | clients should get it as well */ 123 | if (!feed) 124 | { 125 | if (who->type==CLIENT_ATC) clientinterface->sendaa(who, source); else 126 | if (who->type==CLIENT_PILOT) clientinterface->sendap(who, source); 127 | } 128 | } 129 | /* Send an RMCLIENT packet to a destination */ 130 | void servinterface::sendrmclient(absuser *direction, char *dest, client *who, 131 | absuser *ex) 132 | { 133 | char buf[1000]; 134 | sprintf(buf,"%s",who->callsign); 135 | sendpacket(NULL, direction, CMD_RMCLIENT, dest, serverident, packetcount, 136 | 0, 0, buf); 137 | incpacketcount(); 138 | /* This command is important for clients as well, so if it's send to the 139 | broadcast address, clients should get it as well */ 140 | if (!strcmp(dest,"*")) 141 | { 142 | if (who->type==CLIENT_ATC) clientinterface->sendda(who, ex); else 143 | clientinterface->senddp(who, ex); 144 | } 145 | } 146 | void servinterface::sendpilotdata(client *who, absuser *ex) 147 | { 148 | char data[1000]; 149 | sprintf(data,"%s:%s:%d:%d:%.5f:%.5f:%d:%d:%u:%d", who->identflag, 150 | who->callsign, who->transponder, who->rating, who->lat, who->lon, 151 | who->altitude, who->groundspeed, who->pbh, who->flags); 152 | //dolog(L_INFO,"PBH unsigned value is %u",who->pbh); 153 | //dolog(L_INFO,"SendPilotData is sending: %s",data); 154 | sendpacket(NULL, NULL, CMD_PD, "*", serverident, packetcount, 0, 0, data); 155 | clientinterface->sendpilotpos(who, ex); 156 | incpacketcount(); 157 | } 158 | void servinterface::sendatcdata(client *who, absuser *ex) 159 | { 160 | char data[1000]; 161 | sprintf(data,"%s:%d:%d:%d:%d:%.5f:%.5f:%d",who->callsign, 162 | who->frequency, who->facilitytype, who->visualrange, who->rating, 163 | who->lat, who->lon, who->altitude); 164 | sendpacket(NULL, NULL, CMD_AD, "*", serverident, packetcount, 0, 0, data); 165 | clientinterface->sendatcpos(who, ex); 166 | incpacketcount(); 167 | } 168 | 169 | void servinterface::sendcert(char *dest, int cmd, certificate *who, 170 | absuser *direction) 171 | { 172 | char data[1000]; 173 | sprintf(data,"%d:%s:%s:%d:%lu:%s", cmd, who->cid, who->password, who->level, 174 | who->creation,who->origin); 175 | sendpacket(NULL, direction, CMD_CERT, dest, serverident, packetcount, 176 | 0, 0, data); 177 | incpacketcount(); 178 | } 179 | 180 | void servinterface::sendreset() 181 | { 182 | sendpacket(NULL, NULL, CMD_RESET, "*", serverident, packetcount, 0, 0, ""); 183 | packetcount=0; 184 | } 185 | 186 | int servinterface::sendmulticast(client *source, char *dest, char *s, 187 | int cmd, int multiok, absuser *ex) 188 | { 189 | client *destination=NULL; 190 | char data[1000], servdest[100]; 191 | char *sourcestr=(char *)(source?source->callsign:"server"); 192 | if (source&&!STRCASECMP(dest, "server")) 193 | { 194 | switch(cmd) 195 | { 196 | case CL_PING : 197 | clientinterface->sendgeneric(source->callsign, source, NULL, 198 | NULL, "server", s, CL_PONG); 199 | break; 200 | } 201 | return 1; 202 | } 203 | switch (dest[0]) 204 | { 205 | case '@': case '*': 206 | if (!multiok) return 0; 207 | strcpy(servdest, dest); 208 | break; 209 | default: 210 | sprintf(servdest,"%%%s",dest); 211 | destination=getclient(dest); 212 | if (!destination) return 0; 213 | break; 214 | } 215 | sprintf(data,"%d:%s:%s", cmd, sourcestr, s); 216 | sendpacket(NULL, NULL, CMD_MULTIC, servdest, serverident, packetcount, 0, 217 | 0, data); 218 | incpacketcount(); 219 | clientinterface->sendgeneric(dest, destination, ex, source, sourcestr, s, 220 | cmd); 221 | return 1; 222 | } 223 | void servinterface::sendplan(char *dest, client *who, absuser *direction) 224 | { 225 | char buf[1000]; 226 | flightplan *plan=who->plan; 227 | if (!plan) return; 228 | sprintf(buf,"%s:%d:%c:%s:%d:%s:%d:%d:%s:%s:%d:%d:%d:%d:%s:%s:%s", 229 | who->callsign, plan->revision, plan->type, plan->aircraft, 230 | plan->tascruise, plan->depairport, plan->deptime, 231 | plan->actdeptime, plan->alt, plan->destairport, plan->hrsenroute, 232 | plan->minenroute, plan->hrsfuel, plan->minfuel, plan->altairport, 233 | plan->remarks, plan->route); 234 | sendpacket(NULL, direction, CMD_PLAN, dest, serverident, packetcount, 0, 235 | 0, buf); 236 | incpacketcount(); 237 | clientinterface->sendplan(NULL, who, 400); 238 | } 239 | void servinterface::sendweather(char *dest, int fd, wprofile *w) 240 | { 241 | if (!STRCASECMP(dest, serverident)) 242 | { 243 | systeminterface->receiveweather(fd, w); 244 | return; 245 | } 246 | if (dest[0]=='%') 247 | { 248 | client *c=getclient(dest+1); 249 | if (!c) return; 250 | if (c->location==myserver) 251 | { 252 | clientinterface->sendweather(c, w); 253 | return; 254 | } 255 | } 256 | int x; 257 | char data[1000]="", piece[100]; 258 | char weather[1000]; 259 | w->print(weather); 260 | sprintf(data,"%s:%d:%s", w->name, fd, weather); 261 | sendpacket(NULL, NULL, CMD_WEATHER, dest, serverident, packetcount, 262 | 0, 0, data); 263 | incpacketcount(); 264 | } 265 | void servinterface::sendmetar(char *dest, int fd, char *station, char *data) 266 | { 267 | if (!STRCASECMP(dest, serverident)) 268 | { 269 | systeminterface->receivemetar(fd, station, data); 270 | return; 271 | } 272 | if (dest[0]=='%') 273 | { 274 | client *c=getclient(dest+1); 275 | if (!c) return; 276 | if (c->location==myserver) 277 | { 278 | clientinterface->sendmetar(c, data); 279 | return; 280 | } 281 | } 282 | char buf[1000]; 283 | sprintf(buf, "%s:%d:%s", station, fd, data); 284 | sendpacket(NULL, NULL, CMD_METAR, dest, serverident, packetcount, 285 | 0, 0, buf); 286 | incpacketcount(); 287 | } 288 | void servinterface::sendnowx(char *dest, int fd, char *station) 289 | { 290 | if (!STRCASECMP(dest, serverident)) 291 | { 292 | systeminterface->receivenowx(fd, station); 293 | return; 294 | } 295 | if (dest[0]=='%') 296 | { 297 | client *c=getclient(dest+1); 298 | if (!c) return; 299 | if (c->location==myserver) 300 | { 301 | clientinterface->sendnowx(c, station); 302 | return; 303 | } 304 | } 305 | char buf[1000]; 306 | sprintf(buf, "%s:%d", station, fd); 307 | sendpacket(NULL, NULL, CMD_NOWX, dest, serverident, packetcount, 308 | 0, 0, buf); 309 | incpacketcount(); 310 | } 311 | void servinterface::sendaddwp(absuser *direction, wprofile *wp) 312 | { 313 | char buf[1000], weather[1000]; 314 | wp->print(weather); 315 | sprintf(buf, "%s:%u:%s:%s", wp->name, wp->version, wp->origin, weather); 316 | sendpacket(NULL, direction, CMD_ADDWPROF, "*", serverident, packetcount, 317 | 0, 0, buf); 318 | incpacketcount(); 319 | } 320 | void servinterface::senddelwp(wprofile *wp) 321 | { 322 | sendpacket(NULL, NULL, CMD_DELWPROF, "*", serverident, packetcount, 323 | 0, 0, wp->name); 324 | incpacketcount(); 325 | } 326 | void servinterface::sendkill(client *who, char *reason) 327 | { 328 | char data[100]; 329 | if (who->location==myserver) 330 | { 331 | clientinterface->handlekill(who, reason); 332 | return; 333 | } 334 | sprintf(data, "%s:%s", who->callsign, reason); 335 | sendpacket(NULL, NULL, CMD_KILL, who->location->ident, serverident, 336 | packetcount, 0, 1, data); 337 | incpacketcount(); 338 | } 339 | 340 | void servinterface::assemble(char *buf, int cmdnum, 341 | char *to, char *from, int bi, int pc, int hc, char *data) 342 | { 343 | sprintf(buf,"%s:%s:%s:%c%d:%d:%s",cmdnames[cmdnum], to, from, 344 | bi?'B':'U', pc, hc, data); 345 | } 346 | /* Send the packet on its way. This function will also do the routing */ 347 | void servinterface::sendpacket(absuser *exclude, absuser *direction, int cmdnum, 348 | char *to, char *from, int pc, int hc, int bi, char *data) 349 | { 350 | char buf[1000]; 351 | absuser *tempuser; 352 | server *tempserver; 353 | client *tempclient; 354 | 355 | /* variable to determine wheter or not to do the softlimit check on the 356 | server connection output buffer. currently only do the check on 357 | client position updates */ 358 | int slcheck=cmdnum==CMD_PD||cmdnum==CMD_AD; 359 | 360 | /* Increase the hopcount */ 361 | hc++; 362 | 363 | /* Assemble the packet */ 364 | assemble(buf, cmdnum, to, from, bi, pc, hc, data); 365 | 366 | if (direction) 367 | { 368 | direction->uslprintf("%s\n", slcheck, buf); 369 | return; 370 | } 371 | 372 | /* Now look at the destionation field, to determine the route for this 373 | packet */ 374 | switch (to[0]) 375 | { 376 | 377 | case '@' : /* Fallthrough to broadcast */ 378 | 379 | /* This is a broadcast packet. 380 | Note: '*P' and '*A' are broadcast destinations too! */ 381 | case '*' : 382 | /* Reassemble the packet to indicate a broadcast */ 383 | if (!bi) assemble(buf, cmdnum, to, from, 1, pc, hc, data); 384 | #define SERVOK(x) ((x!=exclude)&&(silentok[cmdnum]||((x)->thisserver==NULL||\ 385 | (((x)->thisserver->flags&SERVER_SILENT)==0)))) 386 | for (tempuser=rootuser;tempuser;tempuser=tempuser->next) 387 | if (SERVOK((servuser*)tempuser)) 388 | tempuser->uslprintf("%s\r\n", slcheck, buf); 389 | break; 390 | 391 | /* This is a packet for a pilot. Lookup the pilot, and send in the 392 | direction of the appropriate server */ 393 | case '%' : 394 | for (tempclient=rootclient;tempclient;tempclient=tempclient->next) 395 | if (!STRCASECMP(tempclient->callsign,to+1)) 396 | { 397 | /* We got the pilot, and his connected server, now if we know the 398 | correct path, send it; otherwise we'll have to broadcast the 399 | packet */ 400 | tempserver=tempclient->location; 401 | /* It the pilot is connected to this server, don't send out 402 | the packet to other servers */ 403 | if (tempserver==myserver) break; 404 | if (tempserver->path) 405 | tempserver->path->uslprintf("%s\r\n", slcheck, buf); else 406 | { 407 | /* Reassemble the packet to indicate a broadcast */ 408 | if (!bi) assemble(buf, cmdnum, to, from, 1, pc, hc, data); 409 | for (tempuser=rootuser;tempuser;tempuser=tempuser->next) 410 | if (SERVOK((servuser*)tempuser)) 411 | tempuser->uslprintf("%s\r\n", slcheck, buf); 412 | } 413 | break; 414 | } 415 | break; 416 | 417 | /* This packet is on its way to a single server */ 418 | default : 419 | for (tempserver=rootserver;tempserver;tempserver=tempserver->next) 420 | if (!STRCASECMP(tempserver->ident,to)) 421 | { 422 | /* We got the server, now if we know the correct path, send it; 423 | otherwise we'll have to broadcast the packet */ 424 | if (tempserver->path) 425 | tempserver->path->uslprintf("%s\r\n", slcheck, buf); else 426 | { 427 | /* Reassemble the packet to indicate a broadcast */ 428 | if (!bi) assemble(buf, cmdnum, to, from, 1, pc, hc, data); 429 | for (tempuser=rootuser;tempuser;tempuser=tempuser->next) 430 | if (SERVOK((servuser*)tempuser)) 431 | tempuser->uslprintf("%s\r\n", slcheck, buf); 432 | } 433 | break; 434 | } 435 | break; 436 | } 437 | } 438 | 439 | /* This routine is called whenever a client is dropped. We have to check 440 | here if there's a silent server connected to us. In that case, we'll 441 | have to send him a RMCLIENT */ 442 | 443 | void servinterface::clientdropped(client *who) 444 | { 445 | absuser *temp; 446 | for (temp=rootuser;temp;temp=temp->next) 447 | { 448 | servuser *s=(servuser *)temp; 449 | if (s->thisserver&&(s->thisserver->flags&SERVER_SILENT)) 450 | sendrmclient(s, s->thisserver->name, who, NULL); 451 | } 452 | } 453 | -------------------------------------------------------------------------------- /src/servinterface.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "interface.h" 3 | #include "user.h" 4 | #include "client.h" 5 | #include "certificate.h" 6 | #include "wprofile.h" 7 | 8 | #ifndef SERVINTERFACEHH 9 | #define SERVINTERFACEHH 10 | 11 | class servinterface:public tcpinterface 12 | { 13 | int packetcount, varmcdrops, varinterr; 14 | int varmchandled, varuchandled, varucoverrun, varfailed, varshape, varbounce; 15 | char *serverident; 16 | time_t lastsync; 17 | public: 18 | servinterface(int, char *, char *); 19 | virtual int run(); 20 | virtual void newuser(int, char *, int, int); 21 | void incpacketcount(); 22 | void sendsync(); 23 | void sendservernotify(char *, server *, absuser *); 24 | void sendreqmetar(char *, char *, int, int, server *); 25 | void sendlinkdown(char *); 26 | void sendpong(char *, char *); 27 | void sendping(char *, char *); 28 | void sendaddclient(char *, client *, absuser *, absuser *, int); 29 | void sendrmclient(absuser *, char *, client *, absuser *); 30 | void sendpilotdata(client *, absuser *); 31 | void sendatcdata(client *, absuser *); 32 | void sendcert(char *, int, certificate *, absuser *); 33 | void sendweather(char *, int, wprofile *); 34 | void sendmetar(char *, int, char *, char *); 35 | void sendnowx(char *, int, char *); 36 | void sendaddwp(absuser *, wprofile *); 37 | void senddelwp(wprofile *); 38 | void sendkill(client *, char *); 39 | void sendreset(); 40 | int sendmulticast(client *, char *, char *s, int, int, absuser *); 41 | void sendplan(char *, client *, absuser *); 42 | void assemble(char *, int, char *, char *, int ,int ,int, char *); 43 | 44 | void sendpacket(absuser *, absuser *, int , char *, char *, int, int, 45 | int, char *); 46 | void clientdropped(client *); 47 | friend class servuser; 48 | friend class server; 49 | }; 50 | #endif 51 | -------------------------------------------------------------------------------- /src/servuser.h: -------------------------------------------------------------------------------- 1 | #include "interface.h" 2 | #include "servinterface.h" 3 | 4 | #ifndef SERVUSERHH 5 | #define SERVUSERHH 6 | 7 | extern char *errors[100]; 8 | extern int nerrors; 9 | extern int silentok[]; 10 | 11 | class servuser : public absuser 12 | { 13 | servinterface *parent; 14 | void doparse(char *s); 15 | void feed(); 16 | void list(char *); 17 | int runcmd(int, char **, int); 18 | void execnotify(char **, int); 19 | void execping(char **, int); 20 | void execpong(char **, int); 21 | void execlinkdown(char **, int); 22 | int execaddclient(char **, int); 23 | void execrmclient(char **, int); 24 | void execad(char **, int); 25 | void execpd(char **, int); 26 | int execcert(char **, int); 27 | void execmulticast(char **, int); 28 | int execplan(char **, int); 29 | void execreqmetar(char **, int); 30 | void execweather(char **, int); 31 | void execmetar(char **, int); 32 | void execnowx(char **, int); 33 | void execaddwp(char **, int); 34 | void execdelwp(char **, int); 35 | void execkill(char **, int); 36 | void execreset(char **, int); 37 | int needlocaldelivery(char *); 38 | public: 39 | 40 | server *thisserver; 41 | time_t startuptime; 42 | int clientok; 43 | virtual void parse (char *); 44 | servuser(int, servinterface *, char *, int, int); 45 | virtual ~servuser(); 46 | }; 47 | #endif 48 | -------------------------------------------------------------------------------- /src/support.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #ifdef WIN32 8 | #include 9 | #define _USE_MATH_DEFINES 10 | #include 11 | #else 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #endif 18 | #include 19 | #include 20 | 21 | #include "support.h" 22 | #include "global.h" 23 | #include "server.h" 24 | 25 | int logp=0; 26 | time_t timer=100000; 27 | loghis loghistory[MAXLOG]; 28 | int mrandseed=0; 29 | 30 | void addlog(int level, char *s) 31 | { 32 | if (loghistory[logp].msg) free(loghistory[logp].msg); 33 | if (level>=L_MAX) level=L_MAX-1; 34 | loghistory[logp].msg=strdup(s), loghistory[logp].level=level; 35 | logp=(logp+1)%MAXLOG; 36 | } 37 | void addfile(char *name, char *string,...) 38 | { 39 | char buf[1000]; 40 | FILE *logfile=fopen(name,"a"); 41 | va_list ap; 42 | 43 | if (!logfile) return; 44 | va_start(ap,string); 45 | vsprintf(buf,string,ap); 46 | va_end(ap); 47 | fprintf(logfile, buf); 48 | fclose(logfile); 49 | } 50 | 51 | void dolog(int level, const char *string, ...) 52 | { 53 | char buf[1000], buf2[1200]; 54 | time_t secs=time(NULL); 55 | struct tm *loctime; 56 | static int firsttime=1; 57 | FILE *logfile=fopen(LOGFILE,"a"); 58 | char *sident=(char *)(myserver?myserver->ident:""); 59 | va_list ap; 60 | 61 | if (!logfile) return; 62 | loctime = localtime(&secs); 63 | va_start(ap,string); 64 | vsprintf(buf,string,ap); 65 | va_end(ap); 66 | 67 | sprintf(buf2,"%02d-%02d-%02d %02d:%02d:%02d %s: %s%s", 68 | loctime->tm_mday, loctime->tm_mon+1, loctime->tm_year, 69 | loctime->tm_hour,loctime->tm_min,loctime->tm_sec, sident, 70 | (level bytes from the packet s and puts it into buf */ 132 | void snappacket(char *s, char *buf, int bytes) 133 | { 134 | strncpy(buf,s,bytes); 135 | buf[bytes]='\0'; 136 | } 137 | void findhostname(unsigned long ip, char *buf) 138 | { 139 | hostent *hent=gethostbyaddr((char*)&ip, sizeof(ip), AF_INET); 140 | ip=ntohl(ip); 141 | if (hent) strcpy(buf,hent->h_name); else 142 | sprintf(buf,"%ld.%ld.%ld.%ld", ip>>24, 0xff&(ip>>16), 0xff&(ip>>8), 0xff&ip); 143 | } 144 | char *strupr(char *line) 145 | { 146 | char *p; 147 | for (p=line;*p;p++) *p=toupper(*p); 148 | return line; 149 | } 150 | double dist(double lat1, double lon1, double lat2, double lon2) 151 | { 152 | double dist, dlon=lon2-lon1; 153 | lat1*=M_PI/180.0, lat2*=M_PI/180.0, dlon*=M_PI/180.0; 154 | dist=(sin(lat1)*sin(lat2))+(cos(lat1)*cos(lat2)*cos(dlon)); 155 | if (dist>1.0) dist=1.0; 156 | dist=acos(dist)*60*180/M_PI; 157 | return dist; 158 | } 159 | char *printpart(double part, char p1, char p2, char *buf) 160 | { 161 | char c=part<0.0?p2:p1; 162 | part=fabs(part); 163 | double degrees=floor(part), min, sec; 164 | part-=degrees; part*=60; 165 | min=floor(part); part-=min; part*=60; 166 | sec=part; 167 | sprintf(buf,"%c %02d %02d' %02d\"",c,(int)degrees,(int)min,(int)sec); 168 | return buf; 169 | } 170 | char *printloc(double lat, double lon, char *buf) 171 | { 172 | char north[100], east[100]; 173 | printpart(lat, 'N', 'S', north); 174 | printpart(lon, 'E', 'W', east); 175 | sprintf(buf,"%s %s",north,east); 176 | return buf; 177 | } 178 | void handlealarm(...) 179 | { 180 | timer++; 181 | } 182 | time_t mtime() 183 | { 184 | /*return timer;*/ 185 | return time(NULL); 186 | } 187 | char *sprinttime(time_t now, char *buf) 188 | { 189 | int s=now%60, m=(now%3600)/60, h=now/3600; 190 | sprintf(buf,"%02d:%02d:%02d",h,m,s); 191 | return buf; 192 | } 193 | char *sprintdate(time_t now, char *buf) 194 | { 195 | struct tm *m=localtime(&now); 196 | if (!now) return strcpy(buf, ""); 197 | sprintf(buf,"%02d-%02d-%04d %02d:%02d:%02d", m->tm_mday, m->tm_mon+1, 198 | m->tm_year+1900, m->tm_hour, m->tm_min, m->tm_sec); 199 | return buf; 200 | } 201 | char *sprintgmtdate(time_t now, char *buf) 202 | { 203 | struct tm *m=gmtime(&now); 204 | sprintf(buf, "%02d/%02d/%04d %02d:%02d", m->tm_mday, m->tm_mon+1, m->tm_year+1900, 205 | m->tm_hour, m->tm_min); 206 | return buf; 207 | } 208 | char *sprintgmt(time_t now, char *buf) 209 | { 210 | struct tm *m=gmtime(&now); 211 | sprintf(buf, "%4d%2d%2d%2d%2d%2d", m->tm_year+1900, m->tm_mon+1, 212 | m->tm_mday, m->tm_hour, m->tm_min, m->tm_sec); 213 | char *p; 214 | for (p=buf;*p;++p) 215 | if (*p ==' ') 216 | *p='0'; 217 | return buf; 218 | } 219 | void msrand(int seed) 220 | { 221 | mrandseed=seed; 222 | } 223 | int mrand() 224 | { 225 | mrandseed^=0x22591d8c; 226 | int part1=(mrandseed<<1)&0xffffffff, part2=mrandseed>>31; 227 | mrandseed^=(part1|part2); 228 | if (sizeof(int)>4) mrandseed&=0xffffffff; 229 | return mrandseed; 230 | } 231 | int mround(double val) 232 | { 233 | double frac; 234 | /* quick patch for the floating point exceptions */ 235 | if (val>INT_MAX) return INT_MAX; 236 | 237 | frac=val-((double)((int)val)); 238 | if (frac>0.5) return ((int)val)+1; else 239 | if (frac<-0.5) return ((int)val)-1; else 240 | return (int)val; 241 | } 242 | time_t mgmtime() 243 | { 244 | time_t now=time(NULL); 245 | return mktime(gmtime(&now)); 246 | } 247 | void donothing(int) 248 | { 249 | } 250 | int mconnect(int sockfd, struct sockaddr *serv_addr, int addrlen, int timeout) 251 | { 252 | int result; 253 | #ifdef WIN32 254 | result=connect(sockfd, serv_addr, addrlen); 255 | #else 256 | struct sigaction sanew, saold; 257 | sanew.sa_handler=donothing; 258 | sigemptyset(&sanew.sa_mask); 259 | sanew.sa_flags=0; 260 | sigaction(SIGALRM, &sanew, &saold); 261 | alarm(timeout); 262 | result=connect(sockfd, serv_addr, addrlen); 263 | alarm(0); 264 | sigaction(SIGALRM, &saold, NULL); 265 | #endif 266 | return result; 267 | } 268 | void dblog(char *msg, int num) 269 | { 270 | if (num==0) return; 271 | dolog(L_ERR, "%d,%s", num,msg); 272 | } 273 | -------------------------------------------------------------------------------- /src/support.h: -------------------------------------------------------------------------------- 1 | #include 2 | #ifdef WIN32 3 | #include 4 | #else 5 | #include 6 | #endif 7 | 8 | #ifndef SUPPORTH 9 | #define SUPPORTH 10 | 11 | /* Levels for logging, standard SYSLOG numbers are used here */ 12 | 13 | #define MAXLOG 50 14 | #define L_EMERG 0 15 | #define L_ALERT 1 16 | #define L_CRIT 2 17 | #define L_ERR 3 18 | #define L_WARNING 4 19 | #define L_INFO 5 20 | #define L_DEBUG 6 21 | #define L_MAX 7 22 | 23 | struct loghis 24 | { 25 | char *msg; 26 | int level; 27 | }; 28 | 29 | extern struct loghis loghistory[MAXLOG]; 30 | extern int logp; 31 | 32 | void setconfigfile(char *name); 33 | int findsection(char *section); 34 | int finditem(char *what, char *buf); 35 | char *configgets(char *s, int size); 36 | void dolog(int level, const char *, ...); 37 | void addfile(char *, char *, ...); 38 | int breakpacket(char *, char **, int); 39 | int breakargs(char *, char **, int); 40 | char *catargs(char **, int, char *); 41 | char *catcommand(char **, int, char *); 42 | void snappacket(char *, char *, int); 43 | void findhostname(unsigned long, char *); 44 | char *strupr(char *); 45 | double dist(double, double, double, double); 46 | char *printloc(double, double, char *); 47 | char *sprinttime(time_t, char *); 48 | char *sprintdate(time_t, char *); 49 | char *sprintgmt(time_t now, char *buf); 50 | char *sprintgmtdate(time_t now, char *buf); 51 | int mconnect(int, struct sockaddr *, int, int); 52 | void dblog(char *, int); 53 | time_t mtime(); 54 | void msrand(int); 55 | int mrand(); 56 | int mround(double); 57 | time_t mgmtime(); 58 | #endif 59 | -------------------------------------------------------------------------------- /src/sysinterface.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifndef WIN32 4 | #include 5 | #include 6 | #endif 7 | #include 8 | #include 9 | 10 | #include "interface.h" 11 | #include "sysinterface.h" 12 | #include "global.h" 13 | #include "support.h" 14 | #include "server.h" 15 | #include "protocol.h" 16 | #include "client.h" 17 | #include "sysuser.h" 18 | 19 | /* The client interface class */ 20 | 21 | sysinterface::sysinterface(int port, char *code , char *d): 22 | tcpinterface(port,code,d) 23 | { 24 | } 25 | int sysinterface::run() 26 | { 27 | return tcpinterface::run(); 28 | } 29 | 30 | void sysinterface::newuser(int fd, char *peer, int portnum, int g) 31 | { 32 | insertuser(new sysuser(fd,this,peer,portnum,g)); 33 | } 34 | 35 | void sysinterface::receivepong(char *from, char *data, char *pc, char *hops) 36 | { 37 | int fd; 38 | absuser *temp; 39 | time_t now; 40 | if (sscanf(data,"%d %lu",&fd,&now)!=2) return; 41 | if (fd==-1) return; 42 | for (temp=rootuser;temp;temp=temp->next) if (temp->fd==fd) 43 | { 44 | temp->uprintf("\r\nPONG received from %s: %d seconds (%s,%s)\r\n", 45 | from, mtime()-now, pc, hops); 46 | temp->printprompt(); 47 | return; 48 | } 49 | } 50 | void sysinterface::receiveweather(int fd, wprofile *w) 51 | { 52 | absuser *temp; 53 | if (fd==-2) 54 | { 55 | char buffer[1000], *array[100]; 56 | w->print(buffer); 57 | int count=breakpacket(buffer, array, 100); 58 | wprofile *wp=new wprofile(w->name, mgmtime(), myserver->ident); 59 | wp->loadarray(array, count); 60 | return; 61 | } 62 | for (temp=rootuser;temp;temp=temp->next) if (temp->fd==fd) 63 | { 64 | sysuser *st=(sysuser *)temp; 65 | w->fix(st->lat, st->lon); 66 | ((sysuser *)temp)->printweather(w); 67 | break; 68 | } 69 | } 70 | void sysinterface::receivemetar(int fd, char *wp, char *w) 71 | { 72 | absuser *temp; 73 | for (temp=rootuser;temp;temp=temp->next) if (temp->fd==fd) 74 | { 75 | ((sysuser *)temp)->printmetar(wp,w); 76 | break; 77 | } 78 | } 79 | void sysinterface::receivenowx(int fd, char *st) 80 | { 81 | absuser *temp; 82 | if (fd==-2) 83 | { 84 | wprofile *wp=new wprofile(st, mgmtime(), myserver->ident); 85 | } 86 | for (temp=rootuser;temp;temp=temp->next) if (temp->fd==fd) 87 | { 88 | temp->uprintf("\r\nNo weather available for %s.\r\n", st); 89 | temp->printprompt(); 90 | break; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/sysinterface.h: -------------------------------------------------------------------------------- 1 | #include "interface.h" 2 | #include "wprofile.h" 3 | 4 | #ifndef SYSINTERFACEHH 5 | #define SYSINTERFACEHH 6 | 7 | class sysinterface:public tcpinterface 8 | { 9 | public: 10 | sysinterface(int, char *, char *); 11 | virtual int run(); 12 | virtual void newuser(int, char *, int, int); 13 | void receivepong(char *, char *, char *, char *); 14 | void receiveweather(int fd, wprofile *); 15 | void receivemetar(int fd, char *, char *); 16 | void receivenowx(int fd, char *); 17 | }; 18 | #endif 19 | -------------------------------------------------------------------------------- /src/sysuser.h: -------------------------------------------------------------------------------- 1 | #include "interface.h" 2 | #include "sysinterface.h" 3 | #include "user.h" 4 | #include "wprofile.h" 5 | 6 | #ifndef SYSUSERHH 7 | #define SYSUSERHH 8 | 9 | #define SYS_SERVERS 0 10 | #define SYS_INFORMATION 1 11 | #define SYS_PING 2 12 | #define SYS_CONNECT 3 13 | #define SYS_TIME 4 14 | #define SYS_ROUTE 5 15 | #define SYS_WEATHER 6 16 | #define SYS_DISCONNECT 7 17 | #define SYS_HELP 8 18 | #define SYS_QUIT 9 19 | #define SYS_STAT 10 20 | #define SYS_SAY 11 21 | #define SYS_CLIENTS 12 22 | #define SYS_CERT 13 23 | #define SYS_PWD 14 24 | #define SYS_DISTANCE 15 25 | #define SYS_RANGE 16 26 | #define SYS_LOG 17 27 | #define SYS_WALL 18 28 | #define SYS_DELGUARD 19 29 | #define SYS_METAR 20 30 | #define SYS_WP 21 31 | #define SYS_KILL 22 32 | #define SYS_POS 23 33 | #define SYS_DUMP 24 34 | #define SYS_SERVERS2 25 35 | #define SYS_REFMETAR 26 36 | 37 | class sysuser : public absuser 38 | { 39 | int authorized; 40 | sysinterface *parent; 41 | void doparse(char *s); 42 | void exechelp(char **, int); 43 | void list(char *); 44 | void usage(int, char *); 45 | void execservers(char **, int, int); 46 | void execping(char **, int); 47 | void execconnect(char **, int); 48 | void execdisconnect(char **, int); 49 | void execroute(char **, int); 50 | void execsay(char **, int); 51 | void execclients(char **, int); 52 | void execcert(char **, int); 53 | void execdistance(char **, int); 54 | void exectime(char **, int); 55 | void execweather(char **, int); 56 | void execrange(char **, int); 57 | void execlog(char **, int); 58 | void execpwd(char **, int); 59 | void execwall(char **, int); 60 | void execdelguard(char **, int); 61 | void execmetar(char **, int); 62 | void execwp(char **, int); 63 | void execkill(char **, int); 64 | void execpos(char **, int); 65 | void execdump(char **, int); 66 | void execrefmetar(char **, int); 67 | void information(); 68 | void runcmd(int, char **, int); 69 | void doset(wprofile *, char *, int); 70 | public: 71 | 72 | double lat, lon; 73 | void printweather(wprofile *); 74 | void printmetar(char *, char *); 75 | sysuser(int, sysinterface *, char *, int, int); 76 | virtual void parse (char *); 77 | sysuser(user *); 78 | virtual ~sysuser(); 79 | }; 80 | #endif 81 | -------------------------------------------------------------------------------- /src/user.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifdef WIN32 4 | #include 5 | #else 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #endif 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "user.h" 19 | #include "interface.h" 20 | #include "manage.h" 21 | #include "support.h" 22 | #include "global.h" 23 | 24 | /*******************************************************************/ 25 | /* The USER class. There's not much interesting here, just handling 26 | the low level communication with the clients */ 27 | 28 | absuser::absuser(int d, tcpinterface *p, char *peername, int portnum, int g) 29 | { 30 | next=prev=NULL, killflag=0, fd=d; 31 | inbuf=(char *)malloc(1); 32 | outbuf=(char *)malloc(1); 33 | insize=1; 34 | outsize=1; 35 | *outbuf=0; 36 | *inbuf=0; 37 | blocked=0, prompt=NULL, feedcount=0, feed=-1; 38 | baseparent=p, timeout=0, peer=strdup(peername), port=portnum; 39 | prevfeedcheck=0, guardflag=g, outbufsoftlimit=-1; 40 | setactive(); 41 | } 42 | absuser::~absuser() 43 | { 44 | dolog(L_INFO,"client from %s removed from %s:%s", peer, 45 | baseparent->description, killreasons[killflag]); 46 | manager->incvar(baseparent->varclosed[killflag]); 47 | if (killflag!=KILL_CLOSED&&killflag!=KILL_WRITEERR) 48 | output(); 49 | if (killflag!=KILL_COMMAND&&guardflag) 50 | baseparent->addguard(this); 51 | CLOSESOCKET(fd); 52 | free(inbuf); free(outbuf); 53 | if (prompt) free(prompt); 54 | if (peer) free(peer); 55 | } 56 | void absuser::setactive() 57 | { 58 | lastactive=lastping=mtime(); 59 | } 60 | void absuser::setmasks(fd_set *rmask, fd_set *wmask) 61 | { 62 | FD_SET(fd,rmask); 63 | if (outbuf[0]) FD_SET(fd,wmask); 64 | } 65 | void absuser::input() 66 | { 67 | char buf[1025]; 68 | int bytes=READSOCK(fd,buf,1024); 69 | #ifdef WIN32 70 | if (bytes<0) 71 | if (WSAGetLastError()==WSAEWOULDBLOCK) 72 | { 73 | return; 74 | } 75 | if (bytes==0) 76 | { 77 | kill(KILL_CLOSED); 78 | return; 79 | } 80 | if (bytes<0) 81 | if (WSAGetLastError()!=WSAEWOULDBLOCK) 82 | { 83 | kill(KILL_CLOSED); 84 | return; 85 | } 86 | #else 87 | if ((bytes<0&&errno!=EINTR)||bytes==0) 88 | { 89 | kill(KILL_CLOSED); 90 | return; 91 | } 92 | #endif 93 | if (baseparent->feedstrategy&FEED_IN) feedcount+=bytes; 94 | buf[bytes]='\0'; 95 | int inbufbytes = strlen(inbuf); 96 | 97 | if (insize>4096&&inbufbytes==0) 98 | { 99 | free(inbuf); 100 | insize=2048; 101 | inbuf=(char*) malloc(insize); 102 | *inbuf=0; 103 | } 104 | if (bytes+inbufbytes+1>insize) 105 | { 106 | insize= bytes+inbufbytes+1000; 107 | char* newinbuf = (char*) malloc(insize); 108 | if (inbufbytes) memcpy(newinbuf,inbuf,inbufbytes); 109 | memcpy(&newinbuf[inbufbytes],buf,bytes); 110 | newinbuf[bytes+inbufbytes]=0; 111 | free(inbuf); 112 | inbuf = newinbuf; 113 | } 114 | else 115 | { 116 | memcpy(&inbuf[inbufbytes],buf,bytes); 117 | inbuf[bytes+inbufbytes]=0; 118 | } 119 | } 120 | int absuser::nextline(char *source, char *dest) 121 | { 122 | int len=strcspn(source, "\r\n"); 123 | if (source[len]=='\0') return -1; 124 | while (source[len]=='\n'||source[len]=='\r') 125 | source[len++]='\0'; 126 | char *where=source+len; 127 | if (len=MAXLINELENGTH) return -1; 130 | return where-source; 131 | } 132 | void absuser::output() 133 | { 134 | int len=strlen(outbuf); 135 | int bytes=len>2048?2048:len; 136 | #ifdef WIN32 137 | if (((bytes=::WRITESOCK(fd,outbuf,bytes))<0)&&WSAGetLastError()!=WSAEWOULDBLOCK) 138 | #else 139 | if (((bytes=::WRITESOCK(fd,outbuf,bytes))<0)&&errno!=EINTR) 140 | #endif 141 | { 142 | kill(KILL_WRITEERR); 143 | return; 144 | } 145 | if (baseparent->feedstrategy&FEED_OUT) feedcount+=bytes; 146 | memmove(outbuf,&outbuf[bytes],len-bytes); 147 | outbuf[len-bytes] =0; 148 | /*if (len==bytes) outbuf[0]='\0'; 149 | else memmove(outbuf, outbuf+bytes, len-bytes+1);*/ 150 | } 151 | void absuser::calcfeed() 152 | { 153 | time_t now=mtime(); 154 | int elapsed=now-prevfeedcheck; 155 | double fact1=(double)elapsed/300, fact2=1.0-fact1; 156 | int newfeed=feedcount/elapsed; 157 | if (feed==-1) feed=newfeed; 158 | else feed=(int)((double)newfeed*fact1+(double)feed*fact2); 159 | feedcount=0, prevfeedcheck=now; 160 | int bandwith=feed; 161 | if (baseparent->feedstrategy==FEED_BOTH) bandwith/=2; 162 | if (bandwith>50) outbufsoftlimit=bandwith*30; 163 | } 164 | int absuser::send(char *buf) 165 | { 166 | int outbufbytes=strlen(outbuf); 167 | int bytes=strlen(buf); 168 | /*int len=strlen(outbuf); 169 | int totalnew=size+len+10; 170 | if ((baseparent->outbuflimit!=-1)&&(totalnew>baseparent->outbuflimit)) 171 | return 0; 172 | if (totalnew<(outsize-1000)) outbuf=(char*)realloc(outbuf,outsize=totalnew); 173 | while (totalnew>=outsize) outbuf=(char*)realloc(outbuf,outsize+=1000); 174 | strcat(outbuf,buf);*/ 175 | if (outbufbytes) 176 | { 177 | if (outbufbytes+bytes+1>outsize) 178 | { 179 | outsize= outbufbytes+bytes+1000; 180 | char* ptr = (char*) malloc(outsize); 181 | memcpy(ptr,outbuf,outbufbytes); 182 | memcpy(&ptr[outbufbytes],buf,bytes); 183 | ptr[bytes+outbufbytes]=0; 184 | free(outbuf); 185 | outbuf = ptr; 186 | } 187 | else 188 | { 189 | memcpy(&outbuf[outbufbytes],buf,bytes); 190 | outbuf[bytes+outbufbytes]=0; 191 | } 192 | } 193 | else 194 | { 195 | int sendbytes=bytes>2048?2048:bytes; 196 | #ifdef WIN32 197 | if (((sendbytes=::WRITESOCK(fd,buf,sendbytes))<0)&&WSAGetLastError()!=WSAEWOULDBLOCK) 198 | #else 199 | if (((sendbytes=::WRITESOCK(fd,buf,sendbytes))<0)&&errno!=EINTR) 200 | #endif 201 | { 202 | kill(KILL_WRITEERR); 203 | return 0; 204 | } 205 | if (baseparent->feedstrategy&FEED_OUT) feedcount+=sendbytes; 206 | if (bytes-sendbytes+1>outsize) 207 | { 208 | outsize= bytes-sendbytes+1000; 209 | char* ptr = (char*) malloc(outsize); 210 | memcpy(ptr,&buf[sendbytes],bytes-sendbytes); 211 | ptr[bytes-sendbytes]=0; 212 | free(outbuf); 213 | outbuf = ptr; 214 | } 215 | else 216 | { 217 | memcpy(&outbuf[outbufbytes],&buf[sendbytes],bytes-sendbytes); 218 | outbuf[bytes-sendbytes+outbufbytes]=0; 219 | } 220 | } 221 | return bytes; 222 | } 223 | void absuser::uprintf(char *s, ...) 224 | { 225 | char b[10000]; 226 | if (killflag) return; 227 | va_list ap; 228 | va_start(ap,s); 229 | vsprintf(b,s,ap); 230 | va_end(ap); 231 | send(b); 232 | } 233 | void absuser::uslprintf(char *s, int check, ...) 234 | { 235 | char b[10000]; 236 | if (killflag) return; 237 | va_list ap; 238 | va_start(ap,check); 239 | vsprintf(b,s,ap); 240 | va_end(ap); 241 | if (check&&outbufsoftlimit!=-1&&(strlen(b)+strlen(outbuf))>outbufsoftlimit) 242 | return; 243 | send(b); 244 | } 245 | int absuser::run() 246 | { 247 | char buf[1000]; 248 | int count=0, stat, ok=0; 249 | if (blocked) return 0; 250 | while ((stat=nextline(inbuf,buf))!=-1&&(++count<60)) 251 | { 252 | if (baseparent->floodlimit!=-1&&(feed>baseparent->floodlimit)) 253 | kill(KILL_FLOOD); 254 | parse(buf); 255 | ok=1; 256 | if (blocked) break; 257 | } 258 | if (ok&&stat==-1) printprompt(); 259 | return (stat==-1)?0:1; 260 | } 261 | void absuser::parse(char *s) { } 262 | void absuser::block() 263 | { 264 | blocked=1; 265 | } 266 | void absuser::unblock() 267 | { 268 | blocked=0; 269 | printprompt(); 270 | } 271 | void absuser::setprompt(char *s) 272 | { 273 | if (prompt) free(prompt); 274 | prompt=strdup(s); 275 | } 276 | void absuser::printprompt() 277 | { 278 | if (!prompt||killflag||blocked) return; 279 | uprintf("%s",prompt); 280 | } 281 | void absuser::kill(int reason) 282 | { 283 | killflag=reason; 284 | } 285 | void absuser::sendping() 286 | { 287 | } 288 | -------------------------------------------------------------------------------- /src/user.h: -------------------------------------------------------------------------------- 1 | #ifdef WIN32 2 | #include 3 | #endif 4 | #include 5 | 6 | #ifndef USERHH 7 | #define USERHH 8 | 9 | class tcpinterface; 10 | class absuser 11 | { 12 | protected: 13 | int fd, killflag, insize, outsize,feed, feedcount, guardflag; 14 | int outbufsoftlimit; 15 | time_t lastactive, lastping, prevfeedcheck; 16 | int timeout; 17 | int blocked; 18 | char *peer; 19 | int port; 20 | absuser *next, *prev; 21 | char *inbuf, *outbuf; 22 | char *prompt; 23 | void setmasks(fd_set *, fd_set *); 24 | void input(); 25 | void output(); 26 | int run(); 27 | void setprompt(char *); 28 | void calcfeed(); 29 | virtual void parse(char *); 30 | tcpinterface *baseparent; 31 | 32 | public: 33 | absuser(int, tcpinterface *, char *, int, int); 34 | virtual ~absuser(); 35 | int nextline(char *, char *); 36 | void kill(int); 37 | int send(char *); 38 | void uprintf(char *, ...); 39 | void uslprintf(char *, int, ...); 40 | void block(); 41 | void unblock(); 42 | void printprompt(); 43 | void setactive(); 44 | virtual void sendping(); 45 | friend class tcpinterface; 46 | friend class servinterface; 47 | friend class clinterface; 48 | friend class sysinterface; 49 | friend class sysuser; 50 | friend class server; 51 | }; 52 | #endif 53 | -------------------------------------------------------------------------------- /src/wprofile.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #ifdef WIN32 5 | #define _USE_MATH_DEFINES 6 | #include 7 | #else 8 | #include 9 | #endif 10 | 11 | #include "wprofile.h" 12 | #include "support.h" 13 | #include "global.h" 14 | #include "mm.h" 15 | #include "client.h" 16 | 17 | wprofile *rootprofile=NULL; 18 | 19 | wprofile::wprofile(char *n, time_t c, char *o) 20 | { 21 | creation=c, rawcode=NULL, active=0; 22 | origin=o?strdup(o):(char*)NULL; 23 | winds=(windlayer*)calloc(4, sizeof(windlayer)); 24 | clouds=(cloudlayer*)calloc(2, sizeof(cloudlayer)); 25 | tstorm=(cloudlayer*)calloc(1, sizeof(cloudlayer)); 26 | temps=(templayer*)calloc(4, sizeof(templayer)); 27 | 28 | int x; 29 | winds[0].floor=winds[0].ceiling=-1; 30 | winds[1].floor=2500, winds[1].ceiling=10400; 31 | winds[2].floor=10400, winds[2].ceiling=22600; 32 | winds[3].floor=22700, winds[3].ceiling=90000; 33 | temps[0].ceiling=100; 34 | temps[1].ceiling=10000; 35 | temps[2].ceiling=18000; 36 | temps[3].ceiling=35000; 37 | dewpoint=0; 38 | for (x=0;x<2;x++) clouds[x].floor=clouds[x].ceiling=-1; 39 | tstorm->floor=tstorm->ceiling=-1; 40 | 41 | visibility=15.00, barometer=2950; 42 | next=rootprofile, prev=NULL; 43 | if (next) next->prev=this; 44 | rootprofile=this; 45 | name=strdup(n); 46 | } 47 | wprofile::~wprofile() 48 | { 49 | free(name); 50 | if (rawcode) free(rawcode); 51 | free(winds); free(clouds); free(temps); free(tstorm); 52 | if (origin) free(origin); 53 | if (next) next->prev=prev; 54 | if (prev) prev->next=next; else rootprofile=next; 55 | } 56 | void wprofile::activate() 57 | { 58 | active=1; 59 | } 60 | void wprofile::parsemetar(char **array, int count) 61 | { 62 | int index=0; 63 | char *station; 64 | 65 | /* First field could be 'METAR' */ 66 | if (!array[index]) return; 67 | if (!STRCASECMP(array[index],"metar")) index++; 68 | 69 | /* The station field */ 70 | if (!array[index]) return; 71 | station=array[index++]; 72 | 73 | /* date and time */ 74 | if (!array[index]) return; 75 | if (array[index][strlen(array[index])-1]=='Z') index++; 76 | 77 | /* Here there could be 'AUTO' or 'COR' */ 78 | if (!array[index]) return; 79 | if (!STRCASECMP(array[index],"auto")) index++; else 80 | if (!STRCASECMP(array[index],"cor")) index++; 81 | 82 | /* Wind speed and direction */ 83 | index+=parsewind(array+index, count-index); 84 | 85 | /* Visibility */ 86 | index+=parsevis(array+index, count-index); 87 | 88 | /* Runway visual range */ 89 | index+=parservr(array+index, count-index); 90 | 91 | /* Weather phenomena */ 92 | index+=parsewx(array+index, count-index); 93 | 94 | /* Sky conditions */ 95 | index+=parsesky(array+index, count-index); 96 | 97 | /* Temperature */ 98 | index+=parsetemp(array+index, count-index); 99 | 100 | /* Barometer */ 101 | index+=parsealt(array+index, count-index); 102 | 103 | /* Use dewpoint to fix visibility */ 104 | fixvisibility(); 105 | } 106 | void wprofile::loadarray(char **array, int count) 107 | { 108 | int x; 109 | barometer=atoi(array[0]); array++; 110 | visibility=atof(array[0]); array++; 111 | /* load cloud data */ 112 | for (x=0;x<2;x++) 113 | { 114 | cloudlayer *l=&clouds[x]; 115 | l->ceiling=atoi(array[0]); array++; 116 | l->floor=atoi(array[0]); array++; 117 | l->coverage=atoi(array[0]); array++; 118 | l->icing=atoi(array[0]); array++; 119 | l->turbulence=atoi(array[0]); array++; 120 | } 121 | /* load thunderstorm data */ 122 | tstorm->ceiling=atoi(array[0]); array++; 123 | tstorm->floor=atoi(array[0]); array++; 124 | tstorm->coverage=atoi(array[0]); array++; 125 | tstorm->icing=atoi(array[0]); array++; 126 | tstorm->turbulence=atoi(array[0]); array++; 127 | /* load wind data */ 128 | for (x=0;x<4;x++) 129 | { 130 | windlayer *l=&winds[x]; 131 | l->ceiling=atoi(array[0]); array++; 132 | l->floor=atoi(array[0]); array++; 133 | l->direction=atoi(array[0]); array++; 134 | l->speed=atoi(array[0]); array++; 135 | l->gusting=atoi(array[0]); array++; 136 | l->turbulence=atoi(array[0]); array++; 137 | } 138 | /* load temp data */ 139 | for (x=0;x<4;x++) 140 | { 141 | templayer *l=&temps[x]; 142 | l->ceiling=atoi(array[0]); array++; 143 | l->temp=atoi(array[0]); array++; 144 | } 145 | } 146 | void wprofile::print(char *data) 147 | { 148 | int x; 149 | char piece[100]; 150 | sprintf(data,"%d:%.2f:", barometer, visibility); 151 | /* Add cloud data */ 152 | for (x=0;x<2;x++) 153 | { 154 | cloudlayer *c=&clouds[x]; 155 | sprintf(piece,"%d:%d:%d:%d:%d:", c->ceiling, c->floor, c->coverage, 156 | c->icing, c->turbulence); 157 | strcat(data, piece); 158 | } 159 | /* Add thunderstorm data data */ 160 | cloudlayer *c=tstorm; 161 | sprintf(piece,"%d:%d:%d:%d:%d:", c->ceiling, c->floor, c->coverage, 162 | c->icing, c->turbulence); 163 | strcat(data, piece); 164 | /* Add wind data */ 165 | for (x=0;x<4;x++) 166 | { 167 | windlayer *l=&winds[x]; 168 | sprintf(piece,"%d:%d:%d:%d:%d:%d:", l->ceiling, l->floor, l->direction, 169 | l->speed, l->gusting, l->turbulence); 170 | strcat(data, piece); 171 | } 172 | /* Add temp data */ 173 | for (x=0;x<4;x++) 174 | { 175 | templayer *l=&temps[x]; 176 | sprintf(piece,"%d:%d:", l->ceiling, l->temp); 177 | strcat(data, piece); 178 | } 179 | } 180 | int wprofile::parsewind(char **array, int count) 181 | { 182 | if (!count) return 0; 183 | int len=strlen(array[0]); 184 | if (len<3) return 0; 185 | char *p=array[0]+len-3; 186 | if (STRCASECMP(p+1,"kt")&&STRCASECMP(p,"mps")) return 0; 187 | p[1]='\0'; 188 | p=strchr(array[0],'G'); 189 | if (p) *p='\0', winds[0].gusting=1; 190 | winds[0].speed=atoi(array[0]+3); 191 | winds[0].ceiling=2500; 192 | winds[0].floor=0; 193 | array[0][3]='\0'; 194 | winds[0].direction=atoi(array[0]); 195 | 196 | int a,b; 197 | if (count==1) return 1; 198 | if (sscanf(array[1],"%dV%d",&a, &b)==2) return 2; 199 | return 1; 200 | } 201 | int wprofile::parsevis(char **array, int count) 202 | { 203 | if (!count) return 0; 204 | visibility=10.00; 205 | if (!STRCASECMP(array[0],"M1/4SM")) 206 | { 207 | visibility=0.15; 208 | return 1; 209 | } 210 | if (!STRCASECMP(array[0],"1/4SM")) 211 | { 212 | visibility=0.25; 213 | return 1; 214 | } 215 | if (!STRCASECMP(array[0],"1/2SM")) 216 | { 217 | visibility=0.50; 218 | return 1; 219 | } 220 | if (!STRCASECMP(array[0],"CAVOK") || !strcmp(array[0],"////") || 221 | !strcmp(array[0], "CLR")) 222 | { 223 | visibility=15.00; 224 | clouds[1].ceiling=26000, clouds[1].floor=24000; 225 | clouds[1].icing=clouds[1].turbulence=0; 226 | clouds[1].coverage=1; 227 | return 1; 228 | } 229 | sscanf(array[0],"%f",&visibility); 230 | if (strstr(array[0], "SM")) return 1; 231 | if (count>1&&strstr(array[1], "SM")) return 2; 232 | if (strstr(array[0], "KM")) 233 | { 234 | visibility=visibility/1.609; 235 | return 1; 236 | } 237 | if (count>1&&strstr(array[1], "KM")) 238 | { 239 | visibility=visibility/1.609; 240 | return 2; 241 | } 242 | if (visibility==9999) visibility=15.00; else 243 | visibility=visibility/1609.0; 244 | return 1; 245 | } 246 | int wprofile::parsewx(char **array, int count) 247 | { 248 | int amount=0, x; 249 | char *patterns[]={"+", "-", "VC", "MI", "BL", "PR", "SH", "BC", "TS", "DR", 250 | "FZ", "DZ", "OC", "UP", "RA", "PE", "SN", "GR", "SG", "GS", "BR", "DU", 251 | "FG", "SA", "FU", "HZ", "VA", "PY", "PO", "DS", "SQ", "FC", "SS", "PLUS", 252 | NULL}; 253 | do 254 | { 255 | if (amount==count) break; 256 | for (x=0;patterns[x];x++) 257 | if (!strncmp(array[amount], patterns[x], strlen(patterns[x]))) break; 258 | if (patterns[x]) amount++; 259 | } while (patterns[x]); 260 | return amount; 261 | } 262 | int wprofile::parsesky(char **array, int count) 263 | { 264 | int amount=0, x; 265 | char *patterns[]={"SKC", "CLR", "VV", "FEW", "SCT", "BKN", "OVC", NULL}; 266 | int coverage[]= { 0, 0, 8, 1, 3, 5, 8, 0 }; 267 | do 268 | { 269 | if (amount==count) break; 270 | for (x=0;patterns[x];x++) 271 | if (!strncmp(array[amount], patterns[x], strlen(patterns[x]))) break; 272 | if (patterns[x]) 273 | { 274 | if (amount<2) 275 | { 276 | int base; 277 | char *basestring=array[amount]+strlen(patterns[x]); 278 | if (sscanf(basestring, "%d", &base)==0) base=10; 279 | base*=100; 280 | clouds[amount].coverage=coverage[x]; 281 | clouds[amount].floor=base; 282 | } 283 | amount++; 284 | } 285 | } while (patterns[x]); 286 | if (amount==1) 287 | { 288 | clouds[0].ceiling=clouds[0].floor+3000; 289 | clouds[0].turbulence=17; 290 | } else if (amount>1) 291 | { 292 | if (clouds[1].floor>clouds[0].floor) 293 | clouds[0].ceiling=clouds[0].floor+(clouds[1].floor-clouds[0].floor)/2, 294 | clouds[1].ceiling=clouds[1].floor+3000; else 295 | clouds[1].ceiling=clouds[1].floor+(clouds[0].floor-clouds[1].floor)/2, 296 | clouds[0].ceiling=clouds[0].floor+3000; 297 | clouds[0].turbulence=(clouds[0].ceiling-clouds[0].floor)/175; 298 | clouds[1].turbulence=(clouds[1].ceiling-clouds[1].floor)/175; 299 | } 300 | return amount; 301 | } 302 | int wprofile::parsetemp(char **array, int count) 303 | { 304 | if (!count) return 0; 305 | char *p=strchr(array[0],'/'), *s=array[0]; 306 | if (!p) return 0; 307 | *(p++)='\0'; 308 | if (*s=='M') temps[0].temp=-atoi(s+1); else temps[0].temp=atoi(s); 309 | temps[0].ceiling=100; 310 | if (*p=='M') dewpoint=-atoi(p+1); else dewpoint=atoi(p); 311 | if (temps[0].temp>-10&&temps[0].temp<10) 312 | { 313 | if (clouds[0].ceiling<12000) clouds[0].icing=1; 314 | if (clouds[1].ceiling<12000) clouds[1].icing=1; 315 | } 316 | return 1; 317 | } 318 | int wprofile::parservr(char **array, int count) 319 | { 320 | int amount=0; 321 | while (amount=10.00) 343 | { 344 | visibility+=temps[0].temp-dewpoint-10; 345 | if (visibility<=30.00) 346 | { 347 | if ((int)visibility%5>2) visibility+=5; 348 | visibility-=(int)visibility%5; 349 | } else 350 | { 351 | if ((int)visibility%10>5) visibility+=10; 352 | visibility-=(int)visibility%10; 353 | } 354 | } 355 | } 356 | wprofile *getwprofile(char *name) 357 | { 358 | wprofile *temp; 359 | for (temp=rootprofile;temp;temp=temp->next) if (!STRCASECMP(temp->name,name)) 360 | return temp; 361 | return NULL; 362 | } 363 | #define VAR_UPDIRECTION 0 364 | #define VAR_MIDCOR 1 365 | #define VAR_LOWCOR 2 366 | #define VAR_MIDDIRECTION 3 367 | #define VAR_MIDSPEED 4 368 | #define VAR_LOWDIRECTION 5 369 | #define VAR_LOWSPEED 6 370 | #define VAR_UPTEMP 7 371 | #define VAR_MIDTEMP 8 372 | #define VAR_LOWTEMP 9 373 | int wprofile::getseason(double lat) 374 | { 375 | struct tm *lt; 376 | time_t now=time(NULL); 377 | lt=localtime(&now); 378 | int season; 379 | switch(lt->tm_mon) 380 | { 381 | case 11: case 0: case 1: season=0; break; 382 | case 5: case 6: case 7: season=2; break; 383 | case 2: case 3: case 4: season=1; break; 384 | case 8: case 9: case 10: season=1; break; 385 | } 386 | if (lat<0) 387 | { 388 | if (season==0) season=2; else 389 | if (season==2) season=0; 390 | } 391 | return season; 392 | } 393 | void wprofile::fix(double lat, double lon) 394 | { 395 | double a1=lat; 396 | double a2=fabs(lon/18.0); 397 | int season=getseason(a1), coriolisvar; 398 | int latvar=metarmanager->getvariation(VAR_UPDIRECTION,-25,25); 399 | if (a1>0) winds[3].direction=mround(6*a1+latvar+a2); 400 | else winds[3].direction=mround(-6*a1+latvar+a2); 401 | winds[3].direction=(winds[3].direction+360)%360; 402 | 403 | int maxvelocity; 404 | switch (getseason(a1)) 405 | { 406 | case 0: maxvelocity=120; break; 407 | case 1: maxvelocity=80; break; 408 | case 2: maxvelocity=50; break; 409 | } 410 | 411 | winds[3].speed=mround(fabs(sin(a1*M_PI/180.0))*maxvelocity); 412 | /**************************************/ 413 | 414 | latvar=metarmanager->getvariation(VAR_MIDDIRECTION, 10, 45); 415 | coriolisvar=metarmanager->getvariation(VAR_MIDCOR, 10, 30); 416 | if (a1>0) winds[2].direction=mround(6*a1+latvar+a2-coriolisvar); 417 | else winds[2].direction=mround(-6*a1+latvar+a2-coriolisvar); 418 | winds[2].direction=(winds[2].direction+360)%360; 419 | 420 | winds[2].speed=(int)(winds[3].speed*(metarmanager->getvariation( 421 | VAR_MIDSPEED, 500, 800)/1000.0)); 422 | 423 | /**************************************/ 424 | int coriolisvarlow=coriolisvar+metarmanager->getvariation(VAR_LOWCOR, 425 | 10, 30); 426 | latvar=metarmanager->getvariation(VAR_LOWDIRECTION, 10, 45); 427 | if (a1>0) winds[1].direction=mround(6*a1+latvar+a2-coriolisvarlow); 428 | else winds[1].direction=mround(-6*a1+latvar+a2-coriolisvarlow); 429 | winds[1].direction=(winds[1].direction+360)%360; 430 | 431 | winds[1].speed=(int)((winds[0].speed+winds[1].speed)/2); 432 | 433 | 434 | /**************************************/ 435 | temps[3].temp=-57+metarmanager->getvariation(VAR_UPTEMP, -4, 4); 436 | temps[2].temp=-21+metarmanager->getvariation(VAR_MIDTEMP, -7, 7); 437 | temps[1].temp=-5+metarmanager->getvariation(VAR_LOWTEMP, -12, 12); 438 | } 439 | 440 | void wprofile::genrawcode() 441 | { 442 | char data[1000], piece[100]; 443 | int x; 444 | time_t now=time(NULL); 445 | struct tm *gm=gmtime(&now); 446 | 447 | /* Station */ 448 | sprintf(data, "%s ", name); 449 | 450 | /* Zulu time */ 451 | sprintf(piece, "%02d%02d%02dZ ", gm->tm_mday, gm->tm_hour, gm->tm_min); 452 | strcat(data, piece); 453 | 454 | /* Winds */ 455 | sprintf(piece, "%03d%02dKT ", winds[0].direction, winds[0].speed); 456 | strcat(data, piece); 457 | 458 | /* Visibility */ 459 | sprintf(piece, "%02dSM ", ((int)visibility)); 460 | strcat(data, piece); 461 | 462 | /* Clouds */ 463 | for (x=0;x<2;x++) if (clouds[x].ceiling!=-1) 464 | { 465 | int c=clouds[0].coverage; 466 | sprintf(piece, "%s%03d ", c==0?"CLR":(c==1||c==2)?"FEW":(c==3||c==4)? 467 | "SCT":(c==5||c==6)?"BKN":"OVC", clouds[0].floor/100); 468 | strcat(data, piece); 469 | } 470 | 471 | /* Temp */ 472 | int temperature=temps[0].temp; 473 | int dew=visibility<5?temperature-1:temperature+1; 474 | sprintf(piece, "%s%02d/%s%02d ", temperature<0?"M":"", abs(temperature), 475 | dew<0?"M":"", abs(dew)); 476 | strcat(data, piece); 477 | 478 | /* QNH */ 479 | sprintf(piece, "A%04d", barometer); 480 | strcat(data, piece); 481 | if (rawcode) free(rawcode); 482 | rawcode=strdup(data); 483 | } 484 | -------------------------------------------------------------------------------- /src/wprofile.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "interface.h" 4 | #include "server.h" 5 | #include "client.h" 6 | 7 | #ifndef WPROFILEHH 8 | #define WPROFILEHH 9 | 10 | struct cloudlayer 11 | { 12 | int ceiling, floor; 13 | int coverage; 14 | int icing; 15 | int turbulence; 16 | }; 17 | struct windlayer 18 | { 19 | int ceiling, floor; 20 | int direction, speed; 21 | int gusting, turbulence; 22 | }; 23 | struct templayer 24 | { 25 | int ceiling, temp; 26 | }; 27 | class wprofile 28 | { 29 | public: 30 | wprofile *next, *prev; 31 | time_t creation, version; 32 | int active; 33 | char *rawcode; 34 | windlayer *winds; 35 | cloudlayer *clouds; 36 | templayer *temps; 37 | cloudlayer *tstorm; 38 | int barometer; 39 | float visibility; 40 | int dewpoint; 41 | char *name, *origin; 42 | 43 | wprofile(char *, time_t, char *); 44 | ~wprofile(); 45 | int getseason(double); 46 | void loadarray(char **, int ); 47 | void parsemetar(char **, int); 48 | void print(char *); 49 | void genrawcode(); 50 | void activate(); 51 | int parsewind(char **, int); 52 | int parsevis(char **, int); 53 | int parsewx(char **, int); 54 | int parsesky(char **, int); 55 | int parsetemp(char **, int); 56 | int parservr(char **, int); 57 | int parsealt(char **, int); 58 | void fixvisibility(); 59 | void fix(double , double); 60 | }; 61 | extern wprofile *rootprofile; 62 | wprofile *getwprofile(char *); 63 | #endif 64 | --------------------------------------------------------------------------------